• 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 }
    复制代码
    作者:Richie Zhang
    声明:本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
  • 相关阅读:
    xinetd编程
    我是这样学习Linux下C语言编程的编译命令gcc的使用
    Linux man命令的使用方法
    string.Format出现异常"输入的字符串格式有误"的解决方法
    .net 发送邮件
    cross join
    解决ASP.NET中的各种乱码问题
    网站推广优化教程100条(SEO,网站关键字优化,怎么优化网站,如何优化网站关键字)
    网页中嵌入Excel控件
    C#基础之 集合队列
  • 原文地址:https://www.cnblogs.com/asdyzh/p/9869956.html
Copyright © 2020-2023  润新知