博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
C#中的线程四(System.Threading.Thread)
阅读量:6245 次
发布时间:2019-06-22

本文共 7614 字,大约阅读时间需要 25 分钟。

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 Name15         {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的值变成722             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 IsSynchronized18             {19                 get20                 {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 State2  //{3      //    get { lock (syncRoot) { return state; } }4      //    set { lock (syncRoot) { state = value; } }5   //}
最好的办法是把lock添加到调用State的地方,当然锁定状态递增还有一种更快的方式
(2)Interlocked
1 public int State2   {3     get4      {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         finally11         {12             Monitor.Exit(syncRoot);13         }14     }15     else16     { 17         //didn't get the lock,do something else18     }19 }

 

转载地址:http://zflia.baihongyu.com/

你可能感兴趣的文章
vuex简单示例
查看>>
根据数据库结构生成RzCheckTree
查看>>
hihocoder [Offer收割]编程练习赛8 矩形计数
查看>>
汇编实验九
查看>>
哈夫曼编码
查看>>
go语言学习之闭包函数
查看>>
javax.servlet.http.HttpServletRequest; 不存在
查看>>
类型自动转换规则
查看>>
kvm-控制台登陆配置
查看>>
SpringAOP
查看>>
有赞MySQL自动化运维之路—ZanDB
查看>>
String与常量池(JDK1.8)
查看>>
lightoj 1031(dp)
查看>>
SQL Server转sqlite数据库
查看>>
python print和strip
查看>>
2016学年第一学期软件工程第二次作业
查看>>
Powershell检查邮件队列设置阈值,通过html形式进行邮件告警
查看>>
痞子衡嵌入式:恩智浦i.MXRT系列微控制器量产神器RT-Flash用户指南
查看>>
PHP学习笔记1
查看>>
MySQL学习1
查看>>