}

 static void Debit() {

  //---debit 1000---

  for (int i = 0; i < 10; i++) {

   Monitor.Enter(obj);

   balance -= 100;

   Console.WriteLine('After debiting, balance is {0}', balance);

   Monitor.Exit(obj);

  }

 }

}

The Enter() method of the Monitor class acquires a lock on the specified object, and the Exit() method releases the lock. The code enclosed by the Enter() and Exit() methods is the critical section. The C# lock statement looks similar to the Monitor class; in fact, it is implemented with the Monitor class. The following lock statement, for instance:

lock (obj) {

 balance -= 100;

 Console.WriteLine('After debiting, balance is {0}', balance);

}

Is equivalent to this Monitor class usage:

Monitor.Enter(obj);

try {

 balance -= 100;

 Console.WriteLine('After debiting, balance is {0}', balance);

} finally {

 Monitor.Exit(obj);

}

Now the code looks promising, but the debiting could still result in a negative balance. To resolve this, you need to so some checking to ensure that the debiting does not proceed until there is a positive balance. Here's how:

static void Debit() {

 //---debit 1000---

 for (int i = 0; i < 10; i++) {

  Monitor.Enter(obj);

  if (balance == 0) Monitor.Wait(obj);

  balance -= 100;

  Console.WriteLine('After debiting, balance is {0}', balance);

  Monitor.Exit(obj);

 }

}

When you use the Wait() method of the Monitor class, you release the lock on the object and enter the object's waiting queue. The next thread that is waiting for the object acquires the lock. If the balance is 0, the debit thread would give up control and let the credit thread have the lock.

However, this code modification may result in the scenario shown in Figure 10-7, in which after debiting the balance five times, balance becomes 0. On the sixth time, the lock held by the debit thread is released to the credit thread. The credit thread credits the balance 15 times. At that point, the program freezes. Turns out that the credit thread has finished execution, but the debit thread is still waiting for the lock to be explicitly returned to it.

Figure 10-7 

To resolve this, you call the Pulse() method of the Monitor class in the credit thread so that it can send a signal to the waiting thread that the lock is now released and is now going to pass back to it. The modified code for the Credit() function now looks like this:

static void Credit() {

 //---credit 1500---

 for (int i = 0; i < 15; i++) {

  Monitor.Enter(obj);

  balance += 100;

  if (balance < 0) Monitor.Pulse(obj);

  Console.WriteLine('After crediting, balance is {0}', balance);

  Monitor.Exit(obj);

 }

}

Figure 10-8 shows that the sequence now is correct.

Figure 10-8

The complete program is as follows:

class Program {

 //---used for locking---

 static object obj = new object();

 //---initial balance amount---

 static int balance = 500;

 static void Main(string[] args) {

  Thread t1 = new Thread(new ThreadStart(Debit));

  t1.Start();

  Thread t2 = new Thread(new ThreadStart(Credit));

  t2.Start();

  Console.ReadLine();

 }

 static void Credit() {

  //---credit 1500---

  for (int i = 0; i < 15; i++) {

   Monitor.Enter(obj);

   balance += 100;

   if (balance > 0) Monitor.Pulse(obj);

   Console.WriteLine('After crediting, balance is {0}', balance);

   Monitor.Exit(obj);

  }

 }

 static void Debit() {

  //---debit 1000---

  for (int i = 0; i < 10; i++) {

   Monitor.Enter(obj);

   if (balance == 0) Monitor.Wait(obj);

   balance -= 100;

   Console.WriteLine('After debiting, balance is {0}', balance);

   Monitor.Exit(obj);

  }

Вы читаете C# 2008 Programmer's Reference
Добавить отзыв
ВСЕ ОТЗЫВЫ О КНИГЕ В ИЗБРАННОЕ

0

Вы можете отметить интересные вам фрагменты текста, которые будут доступны по уникальной ссылке в адресной строке браузера.

Отметить Добавить цитату