C#中的线程四(System.Threading.Thread)

C#中的线程四(System.Threading.Thread)

1.最简单的多线程调用 

 System.Threading.Thread类构造方法接受一个ThreadStart委托,改委托不带参数,无返回值

 1 public static void Start1()
 2    {
 3       Console.WriteLine("this is main thread!:{0},{1}", System.Threading.Thread.CurrentThread.CurrentCulture, Thread.CurrentThread.Name);
 4       System.Threading.ThreadStart start = Method1;
 5       Thread thread = new Thread(start);
 6       thread.IsBackground = true;
 7       thread.Start();
 8       Console.WriteLine("main thread other thing...");
 9    }
10 public static void Method1()
11    {
12       Console.WriteLine("this is sub thread!:{0},{1}", System.Threading.Thread.CurrentThread.CurrentCulture, Thread.CurrentThread.Name);
13       Thread.Sleep(TimeSpan.FromSeconds(3));
14       Console.WriteLine("sub thread other thing...");
15    }

注意thread.IsBackground=true,利用Thread创建的线程默认是前台线程,即IsBackground=false,而线程池中的线程是后台线程。

 前台线程和后台线程的区别在于:当主线程执行结束时,若任然有前台线程在执行,则应用程序的进程任然处于激活状态,直到前台线程执行完毕;而换成后台线程,当主线程结束时,后台线程也跟着结束了。

2.给线程传送数据

   这是使用ParameterizedThreadStart 委托来代替ThreadStart委托,ParameterizedThreadStart 委托接受一个带object的参数,无返回值

 1 public static void Start2()
 2  {
 3     Customer c = new Customer { ID = "aaa", Name = "name" };
 4     Console.WriteLine("this is main thread!:{0},{1}", System.Threading.Thread.CurrentThread.CurrentCulture, Thread.CurrentThread.Name);
 5     ParameterizedThreadStart start = Method2;
 6     Thread thread = new Thread(start);
 7     thread.Start(c);
 8     Console.WriteLine("main thread other thing...");
 9  }
10  public static void Method2(object o)
11   {
12     Console.WriteLine("this is sub thread!:{0},{1}", System.Threading.Thread.CurrentThread.CurrentCulture, Thread.CurrentThread.Name);
13     Console.WriteLine(o.ToString());
14     Thread.Sleep(TimeSpan.FromSeconds(3));
15     Console.WriteLine("sub thread other thing...");
16   }

    由此实例可以看出,我们将一个Customer 实例传入了新线程中,新线程可以直接读取此参数的信息。
    当然还有另一种方法也可以将数据传入线程中,创建一个类,把线程的方法定义为实例方法,这样就可以初始化实例的数据,之后启动线程,还是看实例代码:

 1 public static void Start4()
 2    {
 3       Customer c = new Customer();
 4       //调用同一个对象,从而实现资源共享
 5       ThreadStart ts = c.Increase;
 6       Thread[] tArray = new Thread[20];
 7       for (int i = 0; i < 20; i++)
 8        {
 9           tArray[i] = new Thread(ts);
10           tArray[i].Start();
11         }
12        for (int i = 0; i < 20; i++)
13         {
14            tArray[i].Join();
15         }
16        Console.WriteLine(c.Number.ToString());
17         }
18  public static void Method3(object o)
19    {
20        Customer c = o as Customer;
21        //若不上锁,所以每次结果都不同
22        //应该重新建立一个object进行上锁,因为外边还有可能访问到c这个实例
23        lock (c)
24        {
25           for (int j = 0; j < 1000; j++)
26             {
27                c.Number++;
28              }
29        }
30    }

 Customer类的定义如下:

 1 public class Customer
 2     {
 3         public int Number
 4         {
 5             get;
 6             set;
 7         }
 8         public string ID
 9         {
10             get;
11             set;
12         }
13 
14         public string Name
15         {
16             get;
17             set;
18         }
19         public Customer()
20         {
21             Number = 0;
22         }
23         public void Increase()
24         {
25             object o = new object();
26             lock (o)
27             {
28                 for (int i = 0; i < 1000; i++)
29                 {
30                     Number++;
31                 }
32             }
33         }
34     }

3.竞态条件

    来看竞态条件的定义: 如果两个或多个线程访问相同的对象,或者访问不同步的共享状态,就会出现竞态条件。

    竞态条件也是多线程编程的常犯的错误,如果代码不够健壮,多线程编码会出现一些预想不到的结果,我们来根据一个实例来看:

 1 public static void RaceCondition()
 2   {
 3      ThreadStart method = ChangeState;
 4      //这里放出20个线程
 5      for (int i = 0; i < 20; i++)
 6      {
 7         Thread t = new Thread(method);
 8         t.Name = i.ToString() + "aa";
 9         t.Start();
10       }
11    }
12    //2.线程调用的方法,改变状态值
13  public static void ChangeState()
14    {
15       for (int loop = 0; loop < 1000; loop++)
16       {
17          int state = 5;
18          if (state == 5)
19          {
20           //此处第一个线程进入后没来得及++操作,第二个线程又进入,此时第一个线程做了++操作,第二个
21           //线程继续++,state的值变成7
22             state++;
23              if (state == 7)
24              {
25                //没有试验成功
26                Console.WriteLine("state={0},loop={1}", state, loop);
27                Console.WriteLine("thread name:{0}", Thread.CurrentThread.Name);
28               }
29               //Console.WriteLine(state.ToString());
30           }
31        }
32    }

 最简单的解决竞态条件的办法就是使用上锁-lock,锁定共享的对象。用lock语句锁定在线程中共享的变量state,只有一个线程能在锁定块中处理共享的state对象。由于这个对象由所有的线程共享,因此如果一个线程锁定了state,另一个线程就必须等待该锁定的解除。

 1 public static void ChangeState2()
 2    {
 3       object o = new object();
 4       for (int loop = 0; loop < 100; loop++)
 5        {
 6           int state = 5;
 7           lock (o)
 8            {
 9              if (state == 5)
10                {
11                   state++;
12                   if (state == 7)
13                    {
14                       //没有试验成功
15                     Console.WriteLine("state={0},loop={1}", state, loop);
16                     Console.WriteLine("thread name:{0}", Thread.CurrentThread.Name);
17                     }
18                 }
19          }
20       }
21   }

 4.死锁

      在死锁中,至少有两个线程被挂起,等待对方解除锁定。由于两个线程都在等待对方,就出现了死锁,线程将无限等待下去。 

 5.几种同步方法

      上面介绍了两种线程数据共享的办法,一旦需要共享数据,就必须使用同步技术,确保一次只有一个线程访问和改变共享状态。上面介绍了使用lock的方法防止竞态条件的发生,但是如果用不好的话会产生死锁。那么下面再介绍几种针对不同情况使用的线程同步方法。

(1)SyncRoot模式

 下面创建一个类的两个版本,一个同步版本,一个异步版本

 1 public class GeneralDemo
 2  {
 3     public virtual bool IsSynchronized
 4       {
 5         get { return false; }
 6        }
 7     public static GeneralDemo Synchronized(GeneralDemo demo)
 8      {
 9          if (demo.IsSynchronized)
10           {
11             return new SyncDemo(demo);
12            }
13           return demo;
14       }
15     public virtual void DoThis()
16      { }
17    public virtual void DoThat()
18      { }
19  }
 1 //同步版本
 2         private class SyncDemo : GeneralDemo
 3         {
 4             private object syncRoot = new object();
 5             private GeneralDemo demo;
 6             private int state = 0;
 7 
 8             public int State
 9             {
10                 get { return state; }
11                 set { state = value; }
12             }
13             public SyncDemo(GeneralDemo demo)
14             {
15                 this.demo = demo;
16             }
17             public override bool IsSynchronized
18             {
19                 get
20                 {
21                     return true;
22                 }
23             }
24             public override void DoThat()
25             {
26                 lock (syncRoot)
27                 {
28                     demo.DoThis();
29                 }
30             }
31             public override void DoThis()
32             {
33                 lock (syncRoot)
34                 {
35                     demo.DoThis();
36                 }
37             }

需要注意的是在SyncDemo类中,只有方法是同步的,对于这个类的成员调用并没有同步,如果试图用SyncRoot模式锁定对属性的访问,对state的访问变成线程安全的,仍会出现竞态条件

 即这样做是不可取的:

1 //public int State
2  //{
3      //    get { lock (syncRoot) { return state; } }
4      //    set { lock (syncRoot) { state = value; } }
5   //}
最好的办法是把lock添加到调用State的地方,当然锁定状态递增还有一种更快的方式
(2)Interlocked
1 public int State
2   {
3     get
4      {
5        return Interlocked.Increment(ref state);
6      }
7    }

(3)Monitor类

 1 public override void DoThis()
 2 {
 3     if (Monitor.TryEnter(syncRoot, 500))
 4     {
 5         try
 6         {
 7             //acquired the lock
 8             //synchroized region for syncRoot
 9         }
10         finally
11         {
12             Monitor.Exit(syncRoot);
13         }
14     }
15     else
16     { 
17         //didn't get the lock,do something else
18     }
19 }

 

posted @ 2014-04-11 10:25  richiezhang  阅读(2777)  评论(0编辑  收藏  举报