C#中的线程同步----Join方法的使用实例

简介:

最近需要解决一个多线程问题,将这个问题抽象说来就是这个情况。

有A B两个方法同时完成F工作,A B操作均比较耗时,且 B操作需要A操作完成后才能执行。

现 要 A 和B同时运行n个线程。

用数学化的语言描述一下

/*************************** 
* 现有线程 A1,A2……Ai……Ak 
* 线程B1,B2,B3……Bi……Bk 
* Ai和Bi需要顺序执行,共同完成F操作 
* Ai和Bi+1之间无干扰 
* Bi和Bi+1之间无干扰 
* Ai和Bi+1之间无干扰 
* A B操作都比较耗时 
************************/

 

最初在思考解决方案的时候,想的是 为每一A设置一个标记位,去记录 当前的Ai所处的状态,然后再Bi中去循环访问Ai的该标记,如果标记表示可执行Bi,则跳出循环,执行Bi操作。

想来这种方案应该也能实现,但又想到C#提供的Join方法,刚好就是解决t2线程运行前需要等待t1完成的问题而给出的,恰好可以利用,处理起来应该会更简单,更安全。

Join方法是指将指定的线程合并到当前线程中,最初学的时候很不清楚这个合并是什么意思,现在看来应该就是 类似与在 原有的t2一块木板(当前线程)上的某个位置出 拼上 t1木板(指定线程),这样,运行t2木板是,合并上的t1那一块也就成为了必经的路程。

Join方法还提供了Join的时间,以免 t1操作过久,导致一直等待下去。如 t2.Join(100);

考虑上面的问题,由于 Ai和Bi 之间才有同步关系,所以 需要处理就是这一个对应,Join的时候只要找到Bi对应的Ai线程即可

故,我为在整个类中保存两个了静态的 字典 Dictionary<int,Thread> 前一个 表示该线程的索引,=后一个表示该线程本身

源代码如下:

1  
2           public   class   AiBiThread 
3           { 
4                   // 保存A的所有线程   索引,线程对象 
5                   static   Dictionary < int,Thread >   TADic = new   Dictionary < int,Thread > ( ) ; 
6                   // 保存A的所有线程   索引,线程对象 
7                   static   Dictionary < int,   Thread >   TBDic   =   new   Dictionary < int,   Thread > ( ) ; 
8  
9                   public     void   Start ( ) 
10                  { 
11                          // 创建多个A线程 
12                          for ( int   i = 0 ;i &lt; 20;i + + ) 
13                          { 
14                                  Thread   tA = new   Thread ( A ) ; 
15                                  TADic . Add ( i,tA ) ; 
16                                  tA . Start ( i ) ; // 传入线程索引 
17                          } 
18 
19                          // 创建多个B线程 
20                          for   ( int   i   =   0 ;   i   &lt;   20 ;   i + + ) 
21                          { 
22                                  Thread   tB   =   new   Thread ( B ) ; 
23                                  TBDic . Add ( i,tB ) ; 
24                                  tB . Start ( i ) ; // 传入线程索引 
25                          } 
26                          Console . ReadLine ( ) ; 
27                  } 
28 
29                  public     void   A ( object     i ) 
30                  { 
31                          Console . WriteLine ( " 进入线程A-- " + i ) ; 
32                          Console . WriteLine ( " 线程A-- "   +   i + " 正在运行 " ) ; 
33                          Thread . Sleep ( 100 ) ; // 模拟A的耗时,使A停顿大于B,保证进入B时,相应的A尚未运行完毕 
34                          Console . WriteLine ( " 线程A-- " + i + " 要结束了 " ) ; 
35                  } 
36 
37                  public   void   B ( object     i ) 
38                  { 
39                            Console . WriteLine ( " 进入线程B-- "   +   i ) ; 
40                              int   taIndex   =   ( int )   i; 
41                              Console . WriteLine ( " 正在等待线程A-- "   +   i ) ; 
42                              TADic [ taIndex ] . Join ( ) ; // 对相应的A进行Join 
43                              Console . WriteLine ( " 等待线程A-- "   +   i + " 等待完毕!! " ) ; 
44                        
45                          Console . WriteLine ( " 线程B-- "   +   i   +   " 继续运行 " ) ; 
46                          Thread . Sleep ( 50 ) ; // 模拟B的耗时操作 
47                          Console . WriteLine ( " 线程B-- "   +   i   +   " 要结束了 " ) ; 
48                  } 
49          }

 

然后再Main中调用Start方法,整个模拟完毕,输出结果如下:

 

进入线程A--0 
进入线程A--1 
线程A--1正在运行 
进入线程A--2 
线程A--2正在运行 
进入线程A--3 
线程A--3正在运行 
进入线程A--4 
线程A--4正在运行 
线程A--0正在运行 
进入线程A--5 
线程A--5正在运行 
进入线程A--6 
线程A--6正在运行 
进入线程A--7 
线程A--7正在运行 
进入线程A--8 
线程A--8正在运行 
进入线程A--9 
线程A--9正在运行 
进入线程A--10 
线程A--10正在运行 
进入线程A--11 
线程A--11正在运行 
进入线程A--12 
线程A--12正在运行 
进入线程A--13 
线程A--13正在运行 
进入线程A--14 
线程A--14正在运行 
进入线程A--15 
线程A--15正在运行 
进入线程A--16 
线程A--16正在运行 
进入线程A--17 
线程A--17正在运行 
进入线程A--18 
线程A--18正在运行 
进入线程A--19 
线程A--19正在运行 
进入线程B--0 
正在等待线程A--0 
进入线程B--1 
正在等待线程A--1 
进入线程B--2 
正在等待线程A--2 
进入线程B--3 
正在等待线程A--3 
进入线程B--4 
正在等待线程A--4 
进入线程B--5 
正在等待线程A--5 
进入线程B--6 
正在等待线程A--6 
进入线程B--7 
进入线程B--8 
正在等待线程A--8 
正在等待线程A--7 
进入线程B--9 
正在等待线程A--9 
进入线程B--10 
正在等待线程A--10 
进入线程B--12 
正在等待线程A--12 
进入线程B--11 
进入线程B--13 
正在等待线程A--13 
进入线程B--15 
正在等待线程A--15 
进入线程B--14 
正在等待线程A--14 
进入线程B--16 
正在等待线程A--11 
正在等待线程A--16 
进入线程B--18 
正在等待线程A--18 
进入线程B--17 
进入线程B--19 
正在等待线程A--17 
正在等待线程A--19 
线程A--2要结束了 
等待线程A--2等待完毕!! 
线程B--2继续运行 
线程A--1要结束了 
线程A--0要结束了 
线程A--8要结束了 
线程A--10要结束了 
线程A--4要结束了 
线程A--11要结束了 
线程A--6要结束了 
等待线程A--0等待完毕!! 
等待线程A--8等待完毕!! 
线程A--12要结束了 
等待线程A--10等待完毕!! 
等待线程A--4等待完毕!! 
线程A--14要结束了 
线程A--3要结束了 
线程A--7要结束了 
线程A--5要结束了 
等待线程A--1等待完毕!! 
线程A--9要结束了 
线程A--13要结束了 
线程A--15要结束了 
线程A--17要结束了 
等待线程A--11等待完毕!! 
线程A--16要结束了 
等待线程A--6等待完毕!! 
线程B--0继续运行 
线程A--18要结束了 
线程B--8继续运行 
等待线程A--12等待完毕!! 
线程B--10继续运行 
线程B--4继续运行 
等待线程A--14等待完毕!! 
线程A--19要结束了 
等待线程A--3等待完毕!! 
等待线程A--7等待完毕!! 
等待线程A--5等待完毕!! 
线程B--1继续运行 
等待线程A--9等待完毕!! 
线程B--11继续运行 
等待线程A--13等待完毕!! 
等待线程A--15等待完毕!! 
等待线程A--17等待完毕!! 
等待线程A--16等待完毕!! 
线程B--6继续运行 
等待线程A--18等待完毕!! 
线程B--2要结束了 
线程B--12继续运行 
线程B--14继续运行 
等待线程A--19等待完毕!! 
线程B--3继续运行 
线程B--7继续运行 
线程B--5继续运行 
线程B--9继续运行 
线程B--13继续运行 
线程B--15继续运行 
线程B--17继续运行 
线程B--16继续运行 
线程B--18继续运行 
线程B--19继续运行 
线程B--0要结束了 
线程B--8要结束了 
线程B--10要结束了 
线程B--4要结束了 
线程B--1要结束了 
线程B--11要结束了 
线程B--6要结束了 
线程B--12要结束了 
线程B--14要结束了 
线程B--3要结束了 
线程B--7要结束了 
线程B--5要结束了 
线程B--9要结束了 
线程B--13要结束了 
线程B--17要结束了 
线程B--15要结束了 
线程B--19要结束了 
线程B--16要结束了 
线程B--18要结束了

 

验证 原来提出的要求

1. Ai和Bi需要顺序执行

     查看A2和B2的运行过程情况 依次是  
      进入线程A—2    线程A--2正在运行   进入线程B—2  正在等待线程A—2  线程A--2要结束了     等待线程A--2等待完毕!!   线程B--2继续运行    线程B--2要结束了

    验证通过

2. Ai 和Bi+1 无干扰

    查看 A4 和B 5 状态,二者之间无干扰

3.Ai 和Ai+1无干扰,查看 A9 和A 8 状态

4.Bi 和Bi+1无干扰,查看 B9 和B 8 状态

备注 :(其实严格说来 2,3,4 通过这一组数据是得不出这样的结果的,应该是多组数据进行观察。上述234结论只能说是理论分析得来。)

原问题顺利解决。

线程中的Join的问题就暂时说到这里。



本文转自HDDevTeam 51CTO博客,原文链接:http://blog.51cto.com/hddev/624981,如需转载请自行联系原作者

 

相关文章
|
2月前
|
缓存 安全 Java
【JavaEE】——单例模式引起的多线程安全问题:“饿汉/懒汉”模式,及解决思路和方法(面试高频)
单例模式下,“饿汉模式”,“懒汉模式”,单例模式下引起的线程安全问题,解锁思路和解决方法
|
2月前
|
Java 程序员 调度
【JavaEE】线程创建和终止,Thread类方法,变量捕获(7000字长文)
创建线程的五种方式,Thread常见方法(守护进程.setDaemon() ,isAlive),start和run方法的区别,如何提前终止一个线程,标志位,isinterrupted,变量捕获
|
3月前
|
JSON 程序员 C#
使用 C# 比较两个对象是否相等的7个方法总结
比较对象是编程中的一项基本技能,在实际业务中经常碰到,比如在ERP系统中,企业的信息非常重要,每一次更新,都需要比较记录更新前后企业的信息,直接比较通常只能告诉我们它们是否指向同一个内存地址,那我们应该怎么办呢?分享 7 个方法给你!
|
3月前
|
C# UED SEO
C# 异步方法async / await任务超时处理
通过使用 `Task.WhenAny`和 `Task.Delay`方法,您可以在C#中有效地实现异步任务的超时处理机制。这种方法允许您在指定时间内等待任务完成,并在任务超时时采取适当的措施,如抛出异常或执行备用操作。希望本文提供的详细解释和代码示例能帮助您在实际项目中更好地处理异步任务超时问题,提升应用程序的可靠性和用户体验。
121 3
|
4月前
|
Java 开发者
在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口
【10月更文挑战第20天】在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口。本文揭示了这两种方式的微妙差异和潜在陷阱,帮助你更好地理解和选择适合项目需求的线程创建方式。
66 3
|
4月前
|
Java 开发者
在Java多线程编程中,选择合适的线程创建方法至关重要
【10月更文挑战第20天】在Java多线程编程中,选择合适的线程创建方法至关重要。本文通过案例分析,探讨了继承Thread类和实现Runnable接口两种方法的优缺点及适用场景,帮助开发者做出明智的选择。
42 2
|
4月前
|
安全 Java
Java多线程通信新解:本文通过生产者-消费者模型案例,深入解析wait()、notify()、notifyAll()方法的实用技巧
【10月更文挑战第20天】Java多线程通信新解:本文通过生产者-消费者模型案例,深入解析wait()、notify()、notifyAll()方法的实用技巧,包括避免在循环外调用wait()、优先使用notifyAll()、确保线程安全及处理InterruptedException等,帮助读者更好地掌握这些方法的应用。
43 1
|
4月前
|
Java 开发者
Java多线程初学者指南:介绍通过继承Thread类与实现Runnable接口两种方式创建线程的方法及其优缺点
【10月更文挑战第20天】Java多线程初学者指南:介绍通过继承Thread类与实现Runnable接口两种方式创建线程的方法及其优缺点,重点解析为何实现Runnable接口更具灵活性、资源共享及易于管理的优势。
70 1
|
4月前
|
Java
在Java多线程编程中,`wait()`和`notify()`方法的相遇如同一场奇妙的邂逅
在Java多线程编程中,`wait()`和`notify()`方法的相遇如同一场奇妙的邂逅。它们用于线程间通信,使线程能够协作完成任务。通过这些方法,生产者和消费者线程可以高效地管理共享资源,确保程序的有序运行。正确使用这些方法需要遵循同步规则,避免虚假唤醒等问题。示例代码展示了如何在生产者-消费者模型中使用`wait()`和`notify()`。
49 1
|
4月前
|
安全 Java 开发者
Java多线程中的`wait()`、`notify()`和`notifyAll()`方法,探讨了它们在实现线程间通信和同步中的关键作用
本文深入解析了Java多线程中的`wait()`、`notify()`和`notifyAll()`方法,探讨了它们在实现线程间通信和同步中的关键作用。通过示例代码展示了如何正确使用这些方法,并分享了最佳实践,帮助开发者避免常见陷阱,提高多线程程序的稳定性和效率。
81 1