Advanced net programming i 4 th lecture
Download
1 / 38

Advanced .NET Programming I 4 th Lecture - PowerPoint PPT Presentation


  • 93 Views
  • Uploaded on

Advanced .NET Programming I 4 th Lecture. Pavel Je žek [email protected] Locks. Allow to execute complex operations “atomically” (if used correctly).

loader
I am the owner, or an agent authorized to act on behalf of the owner, of the copyrighted work described.
capcha
Download Presentation

PowerPoint Slideshow about ' Advanced .NET Programming I 4 th Lecture' - landon


An Image/Link below is provided (as is) to download presentation

Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author.While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server.


- - - - - - - - - - - - - - - - - - - - - - - - - - E N D - - - - - - - - - - - - - - - - - - - - - - - - - -
Presentation Transcript
Advanced net programming i 4 th lecture

Advanced .NET Programming I4thLecture

Pavel [email protected]


Locks
Locks

  • Allow to execute complex operations “atomically” (if used correctly).

  • Are slow if locking (Monitor.Enter) blocks (implies processor yield) problem for short critical sections – consider spinlocks – .NET structSystem.Threading.SpinLock).


Locks1
Locks

  • Allow to execute complex operations “atomically” (if used correctly).

  • Are slow if locking (Monitor.Enter) blocks (implies processor yield) problem for short critical sections – consider spinlocks – .NET structSystem.Threading.SpinLock).

  • Are slow if locking (Monitor.Enter) will not block (implies new unused syncblock [“lock”] allocation + the locking itself) – again problem for short critical sections – consider lock-free/wait-free algorithms/data structures


Journey to lock free wait free world
Journey to Lock-free/Wait-free World

  • What is C#/.NET’s memory model?

  • Any guaranties of a thread behavior (operation atomicity and ordering) from point of view of other threads?


Atomicity in c
Atomicity in C#

  • Reads and writes of the following data types are atomic: bool, char, byte, sbyte, short, ushort, uint, int, float, and reference types (of the reference itself).

  • Reads and writes of other types, including long, ulong, double, decimal, and user-defined types, are not guaranteed to be atomic.

  • There is no guarantee of atomic read-write(e.g. int a = b; is not atomic).

  • There is definitely no guarantee of atomic read-modify-write (e.g. a++; ).

OK

NO!

NO!

NO!


Interlocked s tatic class
Interlocked Static Class

  • .NET provides explicit atomicity for common read-modify-write scenarios, via “methods” of the Interlocked class:

  • All Interlocked methods are wait-free!


2 threads executing expected output
2 Threads Executing. Expected Output?

int a = 0;

int b = 0;

void t1() {

a = 1;

Console.Write(b);

}

void t2() {

b = 1;

Console.Write(a);

}


2 threads executing expected output1
2 Threads Executing. Expected Output?

OK, compiler can do almost “anything” with this code (e.g. reorder a = 1 after Console.Write(b))! So, let’s suppose we disabled all compiler optimizations.

int a = 0;

int b = 0;

void t1() {

a = 1;

Console.Write(b);

}

void t2() {

b = 1;

Console.Write(a);

}


2 threads executing expected output2
2 Threads Executing. Expected Output?

int a = 0;

int b = 0;

void t1() {

a = 1;

Console.Write(b);

}

void t2() {

b = 1;

Console.Write(a);

}


2 threads executing expected output3
2 Threads Executing. Expected Output?

int a = 0;

int b = 0;

void t1() {

a = 1;

Console.Write(b);

}

void t2() {

b = 1;

Console.Write(a);

}


2 threads executing expected output4
2 Threads Executing. Expected Output?

int a = 0;

int b = 0;

void t1() {

a = 1;

Console.Write(b);

}

void t2() {

b = 1;

Console.Write(a);

}


2 threads executing expected output5
2 Threads Executing. Expected Output?

int a = 0;

int b = 0;

void t1() {

a = 1;

Console.Write(b);

}

void t2() {

b = 1;

Console.Write(a);

}

t1: a = 1

t1: temp1 = b (== 0)

t2: b = 1

t2: temp2 = a (== 1)

t2: Console.Write(temp2) (== 1)

t1: Console.Write(temp1) (== 0)


2 threads executing expected output6
2 Threads Executing. Expected Output?

int a = 0;

int b = 0;

void t1() {

a = 1;

Console.Write(b);

}

void t2() {

b = 1;

Console.Write(a);

}

t1: a = 1 (stored in CPU1 cache)

t2: b = 1 (stored in CPU2 cache)

t1: temp1 = b (== 0 in CPU1 cache)

t2: temp2 = a (== 0 in CPU2 cache)

CPU1: writes back a (== 1) CPU2: sees a == 1

CPU2: writes back b (== 1) CPU1: sees b == 1

t1: Console.Write(temp1) (== 0)

t2: Console.Write(temp2) (== 0)


Concurrent access
Concurrent Access

using System;

usingSystem.Threading;

classTest {

publicstaticint result;

publicstaticbool finished;

staticvoid Thread2() {

result = 123;

finished = true;

}

staticvoid Main() {

finished = false;

newThread(Thread2).Start();

for (;;) {

if (finished) {

Console.WriteLine("result = {0}", result);

return;

}

}

}

}


Concurrent access1
Concurrent Access

using System;

usingSystem.Threading;

classTest {

publicstaticint result;

publicstaticbool finished;

staticvoid Thread2() {

result = 123;

finished = true;

}

staticvoid Main() {

finished = false;

newThread(Thread2).Start();

for (;;) {

if (finished) {

Console.WriteLine("result = {0}", result);

return;

}

}

}

}

or


Concurrent access2
Concurrent Access

Can it be more wrong?

using System;

usingSystem.Threading;

classTest {

publicstaticint result;

publicstaticbool finished;

staticvoid Thread2() {

result = 123;

finished = true;

}

staticvoid Main() {

finished = false;

newThread(Thread2).Start();

for (;;) {

if (finished) {

Console.WriteLine("result = {0}", result);

return;

}

}

}

}

or


Concurrent access3
Concurrent Access

Oh, YES! 

using System;

usingSystem.Threading;

classTest {

publicstaticint result;

publicstaticbool finished;

staticvoid Thread2() {

result = 123;

finished = true;

}

staticvoid Main() {

finished = false;

newThread(Thread2).Start();

for (;;) {

if (finished) {Console.WriteLine("result = {0}", result);return;

}

}

}

}

or

or


Concurrent access4
Concurrent Access

Oh, YES! 

Compiler optimizations rule them all.

using System;

usingSystem.Threading;

classTest {

publicstaticint result;

publicstaticbool finished;

staticvoid Thread2() {

result = 123;

finished = true;

}

staticvoid Main() {

finished = false;

newThread(Thread2).Start();

for (;;) {

if (finished) {Console.WriteLine("result = {0}", result);return;

}

}

}

}

or

or


Concurrent access solution with locks
Concurrent Access – Solution with Locks

usingSystem; usingSystem.Threading;

classTest {

publicstaticint result;

publicstaticbool finished;

staticvoid Thread2() {

lock(???) {

result = 123;

finished = true;

}

}

staticvoid Main() {

finished = false;

newThread(Thread2).Start();

for (;;) {

lock(???) {

if (finished) {

Console.WriteLine("result = {0}", result);

return;

}

}

} } }


Concurrent access wrong solution with locks
Concurrent Access – Wrong Solution with Locks

usingSystem; usingSystem.Threading;

classTest {

publicstaticint result;

publicstaticbool finished;

staticvoid Thread2() {

lock(typeof(Test)) {

result = 123;

finished = true;

}

}

staticvoid Main() {

finished = false;

newThread(Thread2).Start();

for (;;) {

lock(typeof(Test)) {

if (finished) {

Console.WriteLine("result = {0}", result);

return;

}

}

} } }


Concurrent access s till wrong solution with locks
Concurrent Access – Still Wrong Solution with Locks?

classTest{

publicint result;

publicbool finished;

void Thread2() {

lock (this) {

result = 123;

finished = true;

}

}

void Thread1() {

finished = false;

newThread(Thread2).Start();

for(;;){

lock(this){

if (finished) {

Console.WriteLine("result = {0}", result);

return;

}

}

}

}

staticvoid Main() { newTest().Thread1(); }

}


Concurrent access correct wrong solution with locks
Concurrent Access – Correct Wrong Solution with Locks

classTest{

publicint result;

publicbool finished;

privateobjectresultLock = newobject();

void Thread2() {

lock(resultLock){

result = 123;

finished = true;

}

}

void Thread1() {

finished = false;

newThread(Thread2).Start();

for(;;){

lock (resultLock) {

if (finished) {

Console.WriteLine("result = {0}", result);

return;

}

}

}

}

staticvoid Main() { newTest().Thread1(); }

}


Concurrent access5
Concurrent Access

using System;

usingSystem.Threading;

classTest {

publicstaticint result;

publicstaticbool finished;

staticvoid Thread2() {

result = 123;

finished = true;

}

staticvoid Main() {

finished = false;

newThread(Thread2).Start();

for (;;) {

if (finished) {

Console.WriteLine("result = {0}", result);

return;

}

}

}

}

or

or


Concurrent access volatile magic
Concurrent Access – Volatile Magic!

using System;

usingSystem.Threading;

classTest {

publicstaticint result;

publicstaticvolatilebool finished;

staticvoid Thread2() {

result = 123;

finished = true;

}

staticvoid Main() {

finished = false;

newThread(Thread2).Start();

for (;;) {

if (finished) {

Console.WriteLine("result = {0}", result);

return;

}

}

}

}


Volatile access part i
Volatile Access – Part I

  • ECMA: An optimizing compiler that converts CIL to native code shall not remove any volatile operation, nor shall it coalesce multiple volatile operations into a single operation.


V olatile limited compiler optimizations
volatile → Limited Compiler Optimizations

using System;

usingSystem.Threading;

classTest {

publicstaticint result;

publicstaticvolatilebool finished;

staticvoid Thread2() {

result = 123;

finished = true;

}

staticvoid Main() {

finished = false;

newThread(Thread2).Start();

for (;;) {

if (finished) {

Console.WriteLine("result = {0}", result);

return;

}

}

}

}

or


Volatile access part ii
Volatile Access – Part II

  • ECMA/C# Spec: A read of a volatile field is called a volatile read. A volatile read has “acquire semantics”; that is, it is guaranteed to occur prior to any references to memory that occur after it in the instruction sequence.

  • ECMA/C# Spec: A write of a volatile field is called a volatile write. A volatile write has “release semantics”; that is, it is guaranteed to happen after any memory references prior to the write instruction in the instruction sequence.

  • Both constraints visible and obeyed by C# compiler, and CLR/JIT!


Concurrent access volatile access
Concurrent Access – Volatile Access

using System;

usingSystem.Threading;

classTest {

publicstaticint result;

publicstaticvolatilebool finished;

staticvoid Thread2() {

result = 123;

finished = true;

}

staticvoid Main() {

finished = false;

newThread(Thread2).Start();

for (;;) {

if (finished) {

Console.WriteLine("result = {0}", result);

return;

}

}

}

}


Volatile access part iii
Volatile Access – Part III

  • System.Threading.Thread.VolatileRead/VolatileWrite from/to any field ↔ any read/write from/to a volatile field

  • ECMA: Thread.VolatileRead: Performs a volatile read from the specified address. The value at the given address is atomically loaded with acquire semantics, meaning that the read is guaranteed to occur prior to any references to memory that occur after the execution of this method in the current thread. It is recommended that Thread.VolatileReadand Thread.VolatileWritebe used in conjunction. Calling this method affects only this single access; other accesses to the same location are required to also be made using this method or Thread.VolatileWriteif the volatile semantics are to be preserved. This method has exactly the same semantics as using the volatile prefix on the load CIL instruction, except that atomicity is provided for all types, not just those 32 bits or smaller in size.

  • MSDN: Thread.VolatileRead: Reads the value of a field. The value is the latest written by any processor in a computer, regardless of the number of processors or the state of processor cache.

CORRECT

NOT TRUE!


Volatile access part iv
Volatile Access – Part IV

  • System.Threading.Thread.VolatileRead/VolatileWrite from/to any field ↔ read/write from/to a volatile field

  • Volatile read/write is slower than normal read/write → excessive use of volatile fields can degrade performace!

  • System.Threading.Thread.VolatileRead/VolatileWriteallow to do volatile reads/writes only on need-to-do basis – i.e. only in parts of algorithm with data races, or allows volatile writes without volatile reads, etc.


2 threads executing expected output7
2 Threads Executing. Expected Output?

volatile int a = 0;

volatile int b = 0;

void t1() {

a = 1;

Console.Write(b);

}

void t2() {

b = 1;

Console.Write(a);

}


2 threads executing expected output8
2 Threads Executing. Expected Output?

volatile int a = 0;

volatile int b = 0;

void t1() {

a = 1;

Console.Write(b);

}

void t2() {

b = 1;

Console.Write(a);

}


Thread memorybarrier
Thread.MemoryBarrier()

  • ECMA: Guarantees that all subsequent loads or stores from the current thread will not access memory until after all previous loads and stores from the current thread have completed, as observed from this or other threads.

  • MSDN: The processor executing the current thread cannot reorder instructions in such a way that memory accesses prior to the call to MemoryBarrier execute after memory accesses that follow the call to MemoryBarrier.


2 threads executing expected output9
2 Threads Executing. Expected Output?

int a = 0;

int b = 0;

void t1() {

a = 1;

Thread.MemoryBarrier();

Console.Write(b);

}

void t2() {

b = 1;

Thread.MemoryBarrier();

Console.Write(a);

}


2 threads executing expected output10
2 Threads Executing. Expected Output?

Warning: volatile should be still considered in most situations – to avoid C#/JIT compiler optimizations.

volatile int a = 0;

volatile int b = 0;

void t1() {

a = 1;

Thread.MemoryBarrier();

Console.Write(b);

}

void t2() {

b = 1;

Thread.MemoryBarrier();

Console.Write(a);

}


Concurrent access solution with locks1
Concurrent Access – Solution with Locks

Does it really work?

If yes, then why?

usingSystem; usingSystem.Threading;

classTest {

publicstaticint result;

publicstaticbool finished;

privatestaticobjectresultLock = newobject();

staticvoid Thread2() {

lock (resultLock) {

result = 123;

finished = true;

}

}

staticvoid Main() {

finished = false;

newThread(Thread2).Start();

for (;;) {

lock (resultLock) {

if (finished) {

Console.WriteLine("result = {0}", result);

return;

}

}

} } }


Implicit memory barriers
Implicit Memory Barriers

  • Many threading API include an implicit memory barrier (aka memory fence), e.g.:

    • Monitor.Enter/Monitor.Exit

    • Interlocked.*

    • Thread.Start



ad