黑马程序员技术交流社区

标题: 多线程之intelocked互锁 [打印本页]

作者: hehe04    时间: 2012-8-26 18:25
标题: 多线程之intelocked互锁
本帖最后由 hehe04 于 2012-8-26 19:21 编辑

这两天学习多线程的内容,其中有一个很重要的概念,那就是互锁。当多个线程在运行时需要使用到同一资源,那么就需要使用互锁。例如,提示用户输入一句话,然后一个字一个字的将其打印在屏幕上。那么我们需要一个char类型的变量 char1,两个线程,一个用于对char1赋值,一个用于读取char1的值。,我们设计如下代码来实现上述需求。

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Text;
  4. using System.Threading;

  5. namespace 线程同步interlocked
  6. {
  7.     class Program
  8.     {
  9.         private static char result;
  10.         private static string a = "青青子衿,悠悠我心!";

  11.         static void Main(string[] args)
  12.         {
  13.             ThreadStart ts=write;
  14.             ThreadStart ts2 = read;
  15.             Thread readThread = new Thread(ts2);
  16.             Thread writeThread = new Thread(ts);
  17.             writeThread.Start();
  18.             readThread.Start();

  19.         }
  20.         
  21.         static void write()
  22.         {
  23.             for (int i = 0; i < a.Length; i++)
  24.             {
  25.                 result = a[i];
  26.                 Thread.Sleep(20);
  27.             }
  28.         }
  29.         static void read()
  30.         {
  31.             for (int i = 0; i < a.Length; i++)
  32.             {
  33.                 char b = result;
  34.                 Console.Write(b);
  35.                 Thread.Sleep(20);
  36.             }

  37.         }
  38.     }
  39. }
复制代码
这样写,貌似没有问题,但是当我们运行程序是就会发现,输出的文字乱七八糟,并不是我们所输入的原文。这是因为两个线程的执行几率并不是完全等分的,那么我们就需要这样来实现,当写入线程在写入的时候,就锁定这个变量,此时不允许读取线程来读取内容,等写完了这个字符,再解锁。同样的,读取线程也是一样。

interlocked类的部分方法
read() 读取计数器的值
Increment() 使计数器增加1
Decrement()使计数器减小1;
Add() 使计数器增加指定的值
Exchange() 把计数器设置为指定的值
CompareExchange()  先把计数器和某个值进行比较,若相等,就把计数器设为指定值



代码如下。
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Text;
  4. using System.Threading;

  5. namespace 线程同步interlocked
  6. {
  7.     class Program
  8.     {
  9.         private static char result;
  10.         private static string a = "青青子衿,悠悠我心!";

  11.         static void Main(string[] args)
  12.         {
  13.             ThreadStart ts=write;
  14.             ThreadStart ts2 = read;
  15.             Thread readThread = new Thread(ts2);
  16.             Thread writeThread = new Thread(ts);
  17.             writeThread.Start();
  18.             readThread.Start();

  19.         }
  20.         static long num = 0; //定义一个long变量用于计数,必须是long,因为interlocked的增加和减少计数器的方法的参数要求是long类型。
  21.         static void write()
  22.         {
  23.             for (int i = 0; i < a.Length; i++)
  24.             {
  25.                 while (Interlocked.Read(ref num) == 1) //这里必须使用引用来传递参数,当计数器为1时就不断的让他sleep
  26.                 { Thread.Sleep(10); }
  27.                 result = a[i];
  28.                 Interlocked.Increment(ref num);//写完后就让计数器增加1
  29.                
  30.             }
  31.         }
  32.         static void read()
  33.         {
  34.             for (int i = 0; i < a.Length; i++)
  35.             {
  36.                 while (Interlocked.Read(ref num) == 0)
  37.                 { Thread.Sleep(10); }
  38.                 char b = result;
  39.                 Console.Write(b);
  40.                 Interlocked.Decrement(ref num);//读取完就让计数器减少1
  41.             }

  42.         }
  43.     }
  44. }



复制代码
通过以上代码,我们就可以正常的输出字符串内容了。这里有一个问题,如果我不使用interlocked,而是直接操作num的值,也可以正常输出内容。那么是否这也是一种互锁呢,可是在我学习的资料中,并没有提到我的这种方法。是不是我这种写法并没有实现互锁,只是在这种特定情况下,输出内容正常罢了。知道的同学麻烦告知一下,这里先谢过了。代码是这样写的
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Text;
  4. using System.Threading;

  5. namespace 线程同步interlocked
  6. {
  7.     class Program
  8.     {
  9.         private static char result;
  10.         private static string a = "青青子衿,悠悠我心!";

  11.         static void Main(string[] args)
  12.         {
  13.             ThreadStart ts=write;
  14.             ThreadStart ts2 = read;
  15.             Thread readThread = new Thread(ts2);
  16.             Thread writeThread = new Thread(ts);
  17.             writeThread.Start();
  18.             readThread.Start();

  19.         }
  20.         static long num = 0;
  21.         static void write()
  22.         {
  23.             for (int i = 0; i < a.Length; i++)
  24.             {
  25.                 //while (Interlocked.Read(ref num) == 1)
  26.                 //{ Thread.Sleep(10); }
  27.                 while (num == 1)
  28.                 { Thread.Sleep(10); }
  29.                 result = a[i];
  30.                 //Interlocked.Increment(ref num);
  31.                 num += 1;
  32.             }
  33.         }
  34.         static void read()
  35.         {
  36.             for (int i = 0; i < a.Length; i++)
  37.             {
  38.                 //while (Interlocked.Read(ref num) == 0)
  39.                 //{ Thread.Sleep(10); }
  40.                 while (num == 0)
  41.                 { Thread.Sleep(10); }
  42.                 char b = result;
  43.                 Console.Write(b);
  44.                 num = num - 1;
  45.                 //Interlocked.Decrement(ref num);
  46.             }

  47.         }
  48.     }
  49. }
复制代码

作者: 瞿正峰    时间: 2012-8-29 09:59
num 最后那个中实现  
也就是直接操作num的值
直接操作NUM是在内存中进行操作
InterLocked.Increment 是在 CPU寄存器内操作
InterLocked.Increment 区别在于 你开两个进程   
能通过寄存器同步 ,而 直接操作NUM是在内存中操作
开两个进程 他们在内存中生成的NUM变量是无法共享的
作者: 许庭洲    时间: 2012-8-29 10:31
1. Interlocked类的成员来实现线程同步;
2. Interlocked类提供了同步对多个线程共享的变量的访问的方法;
3. Interlocked类提供了Increment和Decrement方法递增或递减某个变量并返回结果;
4. 如果我不使用interlocked,而是直接操作num的值可能会发生死锁;
5. 直接操作num只不过是通过设置标志位来判断线程间实现同步,一旦线程较多,程序会崩溃。




欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2