Każdy jest innym i nikt sobą samym.

2 Synchronization
At times, you might want to control access to a resource, such as an object's properties or methods, so that only one thread at a time can modify or use that resource. Your object is similar to the airplane restroom discussed earlier, and the various threads are like the people waiting in line.
Synchronization is provided by a lock on the object, which prevents a second thread from barging in on your object until the first thread is finished with it.
In this section you'll examine three synchronization mechanisms provided by the CLR: the Interlock class, the C# lock statement, and the Monitor class. But first, you'll need to simulate a shared resource, such as a file or printer, with a simple integer variable: counter. Rather than opening the file or accessing the printer, you'll increment counter from each of two threads.
To start, declare the member variable and initialize it to 0:
int counter = 0;
Modify the Incrementer method to increment the counter member variable:
public void Incrementer( )
{
try
{
while (counter < 1000)
{
int temp = counter;
temp++; // increment

// simulate some work in this method
Thread.Sleep(1);

page 428
Programming C#

// assign the Incremented value
// to the counter variable
// and display the results
counter = temp;
Console.WriteLine(
"Thread {0}. Incrementer: {1}",
Thread.CurrentThread.Name,
counter);
}
}
The idea here is to simulate the work that might be done with a controlled resource. Just as we might open a file, manipulate its contents, and then close it, here we read the value of counter into a temporary variable, increment the temporary variable, sleep for one millisecond to simulate work, and then assign the incremented value back to counter.
The problem is this: your first thread will read the value of counter (0) and assign that to a temporary variable. It will then increment the temporary variable. While it is doing its work, the second thread will read the value of counter (still 0) and assign that value to a temporary variable.
The first thread finishes its work, then assigns the temporary value (1) back to counter and displays it. The second thread does the same. What is printed is 1,1. In the next go around, the same thing happens. Rather than having the two threads count 1,2,3,4, we see 1,1,2,2,3,3. Example 20-3
shows the complete source code and output for this example.
Example 20-3. Simulating a shared resource
namespace Programming_CSharp
{
using System;
using System.Threading;

class Tester
{
private int counter = 0;

static void Main( )
{
// make an instance of this class
Tester t = new Tester( );

// run outside static Main
t.DoTest( );
}

public void DoTest( )
{
Thread t1 = new Thread( new ThreadStart(Incrementer) );
t1.IsBackground=true;
t1.Name = "ThreadOne";
t1.Start( );
Console.WriteLine("Started thread {0}",
t1.Name);

Thread t2 = new Thread( new ThreadStart(Incrementer) );
t2.IsBackground=true;
t2.Name = "ThreadTwo";
t2.Start( );
Console.WriteLine("Started thread {0}",
t2.Name);
t1.Join( );

page 429
Programming C#
t2.Join( );

// after all threads end, print a message
Console.WriteLine("All my threads are done.");
}

// demo function, counts up to 1K
public void Incrementer( )
{
try
{
while (counter < 1000)
{
int temp = counter;
temp++; // increment

// simulate some work in this method
Thread.Sleep(1);

// assign the decremented value
// and display the results
counter = temp;
Console.WriteLine(
"Thread {0}. Incrementer: {1}",
Thread.CurrentThread.Name,
counter);
}
}
catch (ThreadInterruptedException)
{
Console.WriteLine(
"Thread {0} interrupted! Cleaning up...",
Thread.CurrentThread.Name);
}
finally
{
Console.WriteLine(
"Thread {0} Exiting. ",
Thread.CurrentThread.Name);
}
}
}
}

Output:

Started thread ThreadOne
Started thread ThreadTwo
Thread ThreadOne. Incrementer: 1
Thread ThreadOne. Incrementer: 2
Thread ThreadOne. Incrementer: 3
Thread ThreadTwo. Incrementer: 3
Thread ThreadTwo. Incrementer: 4
Thread ThreadOne. Incrementer: 4
Thread ThreadTwo. Incrementer: 5
Thread ThreadOne. Incrementer: 5
Thread ThreadTwo. Incrementer: 6
Thread ThreadOne. Incrementer: 6
Assume your two threads are accessing a database record rather than reading a member variable.
For example, your code might be part of an inventory system for a book retailer. A customer asks if Programming C# is available. The first thread reads the value and finds that there is one book on page 430
Programming C#
hand. The customer wants to buy the book, so the thread proceeds to gather credit card information and validate the customer's address.
While this is happening, a second thread asks if this wonderful book is still available. The first thread has not yet updated the record, so one book still shows as available. The second thread begins the purchase process. Meanwhile, the first thread finishes and decrements the counter to zero. The second thread, blissfully unaware of the activity of the first, also sets the value back to zero. Unfortunately, you have now sold the same copy of the book twice.
As noted earlier, you need to synchronize access to the counter object (or to the database record, file, printer, etc.).
20.2.1 Using Interlocked
The CLR provides a number of synchronization mechanisms. These include the common synchronization tools such as critical sections (called Locks in .NET), as well as more sophisticated tools such as a Monitor class. Each is discussed later in this chapter.