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());
  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());
  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[];
  7. for (int i = ; i < ; i++)
  8. {
  9. tArray[i] = new Thread(ts);
  10. tArray[i].Start();
  11. }
  12. for (int i = ; i < ; 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 = ; j < ; 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 = ;
  22. }
  23. public void Increase()
  24. {
  25. object o = new object();
  26. lock (o)
  27. {
  28. for (int i = ; i < ; 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 = ; i < ; 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 = ; loop < ; loop++)
  16. {
  17. int state = ;
  18. if (state == )
  19. {
  20. //此处第一个线程进入后没来得及++操作,第二个线程又进入,此时第一个线程做了++操作,第二个
  21. //线程继续++,state的值变成7
  22. state++;
  23. if (state == )
  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 = ; loop < ; loop++)
  5. {
  6. int state = ;
  7. lock (o)
  8. {
  9. if (state == )
  10. {
  11. state++;
  12. if (state == )
  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 = ;
  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, ))
  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. }

C#中的线程四(System.Threading.Thread)的更多相关文章

  1. MVC4 + EF + System.Threading.Thread 出现的问题记录

    项目要求是页面监测到后台数据库用户数据(Users)变化,前台做出相应的响应和操作. 一.参考很多资料,大概有几种方式: 参考资料地址:http://www.cnblogs.com/hoojo/p/l ...

  2. 异常System.Threading.Thread.AbortInternal

    异常信息: System.Threading.ThreadAbortException: 正在中止线程. 在 System.Threading.Thread.AbortInternal() 在 Sys ...

  3. c# System.Threading.Thread

    using System; using System.Threading; // Simple threading scenario: Start a static method running // ...

  4. System.Threading.Thread的使用及传递参数等总结

    using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threa ...

  5. 【Java中的线程】java.lang.Thread 类分析

    进程和线程 联想一下现实生活中的例子--烧开水,烧开水时是不是不需要在旁边守着,交给热水机完成,烧开水这段时间可以去干一点其他的事情,例如将衣服丢到洗衣机中洗衣服.这样开水烧完,衣服洗的也差不多了.这 ...

  6. .Net 指定时间段内定时执行的Windows服务(System.Threading.Thread)

    创建一个Windows服务项目:解决方案(右击)——> 添加 ——> 新建项目——>项目类型选择Windows——>模板选择Windows服务 ,如图: 编写Windows服务 ...

  7. 【.Net 学习系列】-- .Net 指定时间段内定时执行的Windows服务(System.Threading.Thread)

    创建一个Windows服务项目:解决方案(右击)——> 添加 ——> 新建项目——>项目类型选择Windows——>模板选择Windows服务 ,如图: 编写Windows服务 ...

  8. 【异常记录(九)】 System.Threading.ThreadAbortException: 正在中止线程

    报错如下: System.Threading.ThreadAbortException: Thread was being aborted. at System.Threading.Thread.Ab ...

  9. C#中假设正确使用线程Task类和Thread类

    C#中使用线程Task类和Thread类小结 刚接触C#3个月左右.原先一直使用C++开发.由于公司的须要,所地採用C#开发.主要是控制设备的实时性操作,此为背景. 对于C#中的Task和Thread ...

随机推荐

  1. C++面试中string类的一种正确简明的写法

    本文首发于酷壳网 http://coolshell.cn/articles/10478.html 先说说程序员(应届生)面试的一般过程,一轮面试(面对一到两个面试官)一般是四.五十分钟,面试官会问两三 ...

  2. Android(Xamarin)之旅(五)

    2016年1月23日,北京迎来了很痛苦的一天,冻死宝宝了,一天都没有出我自己的小黑屋,在这屋子里自娱自乐.不知道你们呢 对于android的四大基本组件(Activity.Service.Broadc ...

  3. maven认证信息

    上传至本地文件夹 <distributionManagement>         <repository>             <id>test</id ...

  4. java定时任务实现

    在java中需要使用到定时任务是,可以通过quartz来实现.

  5. 关于Spring和mybatis的整合

    Spring同Mybatis的整合 1.引入相应的jar包.(Mybatis的jar包,Spring的jar包,mybatis-spring-1.1.1.jar). 2.编写相应的包(三层的包).搭建 ...

  6. javascript 日期转换为中文

    function CNDateString(date) { var cn = ["〇","一","二","三",&quo ...

  7. 微信小程序 设计理念指南

    在此处输入标题   微信小程序的几条开发建议 功能简约,场景贴近随用随走: 操作快捷方便,交互简单: 程序本身代码资源等文件大小限制在1MB之内,这是微信目前的硬限制,目的是为了使得最终到达用户设备上 ...

  8. css大小单位px em rem的转换和详解

    css大小单位px em rem的转换和详解 PX特点1. IE无法调整那些使用px作为单位的字体大小:2. 国外的大部分网站能够调整的原因在于其使用了em或rem作为字体单位:3. Firefox能 ...

  9. Android Sqlite数据库相关——实现 Sqlite 数据库版本升级

    继承SQLiteOpenHelper类后,实现其中的onUpgrade 方法 @Override public void onUpgrade(SQLiteDatabase db, int oldVer ...

  10. 安卓奇葩问题之.so库加载不了

    真是哔了狗了. 今天突然遇到一个问题:之前用第三方的密码控件,给了一个.so库文件.然后我就放在了/jniLibs/armeabi目录下. 运行,一切都很OK. 然后重点来了.N天之后的今天,突然打包 ...