C# 线程会合实例
有这样一个题目:四个线程t1,t2,t3,t4,向4个文件中写入数据,要求:t1只能写入“1”,t2只能写入“2”,t3只能写入“3”,t4只能写入“4”,对4个文件A,B,C,D写入如下内容:
- A:123412341234.....
- B:234123412341....
- C:341234123412....
- D:412341234123....
简单分析一下,对于A文件,t1写入“1”后,我们希望通知t2来写“2”,并且t1前往D文件等着去写“1”,以此类推。
1. 通过等待句柄实现
显然可以用等待句柄来实现,通知t2来写“2”相当于在一个等待句柄上调用 Set 方法,等待在D文件上写“1”相当于在另一等待句柄上调用了 WaitOne 方法,下边是利用4个 AutoResetEvent 来实现它:
class Program
{
private static List<StreamWriter> _sws; private static List<AutoResetEvent> _whs; private static void Main(string[] args)
{
var fileNames = new List<string> {"A", "B", "C", "D"}; // 创建或清空文件
fileNames.ForEach(name =>
{
if (!File.Exists(name))
File.Create(name).Close();
else
{
using (var sw = new StreamWriter(name))
sw.Write("");
}
}); _sws = fileNames.Select(File.AppendText).ToList(); // 为每个文件写入建立一个等待句柄
_whs = fileNames.Select(name => new AutoResetEvent(false)).ToList(); // 创建并启4个线程执行任务
var threads = new List<Thread> {
new Thread(() => Work()), new Thread(() => Work()), new Thread(() => Work()), new Thread(() => Work())
};
threads.ForEach(t => t.Start()); // 等待线程结束并关闭 StreamWrite
threads.ForEach(t => t.Join());
Console.WriteLine("任务完成!");
_sws.ForEach(sw => sw.Close());
} static void Work(int threadIndex)
{
var next = threadIndex - ;
// 为让程序能结束,就打印100次
for (int i = ; i < ; i++)
{
var wh = _whs[next];
var sw = _sws[next];
lock (sw)
{
sw.Write(threadIndex);
}
next = (next - ) < ? : next - ; WaitHandle.SignalAndWait(wh, _whs[next]); //在wh上发信号,并在下一个等待句柄上等待执行
}
}
}
上述例子中我们创建了4个线程来分别打印1,2,3,4,并且为每个文件的写入创建了4个等待句柄来进行信号通信。最后主线程在等待所有线程结束后,关闭文件流。为让程序能正常结束,在 Work 方法中就只循环写100次。
以t1(列表中第一个线程)为例,在A文件中打印1后,调用 WaitHandle.SignalAndWait(wh,wh[next]),即在wh上发信号通知可以接着写入了,并在下一个等待句柄上等待写入信号。
关于 SinalAndWait 的可以参见 Thread in C# 或者对应的 中文翻译 。
2. 通过 Barrier 类实现
除了通过等待句柄可以实现题目要求外,同样可以通过 Wait 和 Pulse 来实现。如果是FrameWork 4.0或更高的版本,可以通过 Barrier 类(它是建立在 Wait / Pulse 和自旋锁基础上的)更简单的实现这个题目。
class Program
{
private static Barrier _barrier; private static void Main(string[] args)
{
var fileNames = new List<string> { "A", "B", "C", "D" }; // 创建或清空文件
fileNames.ForEach(name =>
{
if (!File.Exists(name))
File.Create(name).Close();
else
{
using (var sw = new StreamWriter(name))
sw.Write("");
}
}); // 在_barrier上调用SignalAndWait的线程会被阻塞直到这个方法被调用4次
_barrier = new Barrier(); _sws = fileNames.Select(File.AppendText).ToList(); // 创建并启4个线程执行任务
var threads = new List<Thread> {
new Thread(() => Work()), new Thread(() => Work()), new Thread(() => Work()), new Thread(() => Work())
};
threads.ForEach(t => t.Start()); // 等待线程结束并关闭 StreamWrite
threads.ForEach(t => t.Join());
Console.WriteLine("任务完成!");
_sws.ForEach(sw => sw.Close());
} static void Work(int threadIndex)
{
var next = threadIndex - ;
for (int i = ; i < ; i++)
{
var sw = _sws[next];
lock (sw)
{
sw.Write(threadIndex);
}
_barrier.SignalAndWait();
next = (next - ) < ? : next - ;
}
}
}
使用了一个 Barrier 类来替代4个等待句柄,线程调用 SignalAndWait 后会阻塞,直到这个方法被调用4次。在这个例子中意味着4个线程总是在同步进行着打印,下图可以很好的解释 Barrier 类:

关于 Barrier 类,可以参见 Thread in C# 或者对应的 中文翻译 。
C# 线程会合实例的更多相关文章
- java线程池实例
目的 了解线程池的知识后,写个线程池实例,熟悉多线程开发,建议看jdk线程池源码,跟大师比,才知道差距啊O(∩_∩)O 线程池类 package thread.pool2; impor ...
- Java线程基础实例
概述 Java线程是一个在实战开发中经常使用的基础功能,而在Java中线程相关的类在java.lang和java.util.concurrent里 Thread package thread.base ...
- 简单的C#线程开发实例(隔一秒改变一下Label的Text)
要实现的效果:点击按纽,窗口上的label上出现1~100数字的变化. 第一个实例(把窗口上的label上文字改成0): using System; using System.Windows.Form ...
- Java5中的线程池实例讲解
Java5增加了新的类库并发集java.util.concurrent,该类库为并发程序提供了丰富的API多线程编程在Java 5中更加容易,灵活.本文通过一个网络服务器模型,来实践Java5的多线程 ...
- Asp.net 使用线程池实例
实际开发经常会使用线程,如果每次使用都是创建线程.启动线程,然后销毁线程,从性能上来讲,非常占用系统开销,当线程达到一定量的时候会影响程序的运行和处理效率. 使用线程池:好处:线程池是一种多线程处理形 ...
- 一个handle使用更新线程的实例
handle更新线程实例 package com.example.administrator.handle; import android.app.Activity;import android.os ...
- C#一个简单的关于线程的实例
很多初学者听到线程会觉得晦涩难懂,很多资料一堆专有名词也是让人心烦意乱,本着学习加分享的态度,这里做一个简单的实例分享帮助初学者们初识多线程. 首先大概讲述一下多线程和多进程的区别,任务管理器里各种 ...
- 在MFC里面实现线程的实例
线程是一种从软件到硬件的技术,主要目的是为了提高运行速度,和多任务. ××××××××××××××××××××××××××××××××××××需要储备的资料(他人的)××××××××××××××××× ...
- Linux C++线程池实例
想做一个多线程服务器测试程序,因此参考了github的一些实例,然后自己动手写了类似的代码来加深理解. 目前了解的线程池实现有2种思路: 第一种: 主进程创建一定数量的线程,并将其全部挂起,此时线程状 ...
随机推荐
- Android之省市区三级联动
最近项目要做一个电商APP,选择收货地址的三级联动滚动选择组件, 控件用起来非常简单 ,下面是它的运行效果: 布局 <LinearLayout xmlns:android="http: ...
- Population Size CodeForces - 416D (贪心,模拟)
大意: 给定$n$元素序列$a$, 求将$a$划分为连续的等差数列, 且划分数尽量小. $a$中的$-1$表示可以替换为任意正整数, 等差数列中必须也都是正整数. 贪心策略就是从前到后尽量添进一个等差 ...
- TCP-IP详解:Nagle算法
在使用一些协议通讯的时候,比如Telnet,会有一个字节字节的发送的情景,每次发送一个字节的有用数据,就会产生41个字节长的分组,20个字节的IP Header 和 20个字节的TCP Header, ...
- HDOJ1009
#include "iostream" #include "algorithm" #include "cstdio" using names ...
- js传入和传出参数乱码
向js传入参数乱码问题 第一种解决方法 当Js中输出内容中包含中文,可能会导致出现乱码. 如何解决: 1. 设置页面编码: Html代码 <meta http-equiv="Conte ...
- mysql 查询某一主键在那些表中中被设置为外键了
use information_schema; show tables; select * from KEY_COLUMN_USAGE where COLUMN_NAME='areaid';
- jquery插件Loadmask
Loadmask是一个jquery plugin,使用此插件可以在DOM元素加载或更改内容时为此DOM元素添加一个屏蔽层,以防止用户互动,同时起到提醒用户后台任务正在运行的作用. 它可实现的效果:
- Pandas DataFrame 数据选取和过滤
This would allow chaining operations like: pd.read_csv('imdb.txt') .sort(columns='year') .filter(lam ...
- 信号处理函数的返回sigsetjmp/siglongjmp
由于在信号处理期间自动屏蔽了正在被处理的信号,而使用setjmp/longjmp跳出信号处理程序时又不会自动将 信号屏蔽码修改会原来的屏蔽码,从而引起该信号被永久屏蔽. 可以使用sigsetjmp/s ...
- L1-030 一帮一
“一帮一学习小组”是中小学中常见的学习组织方式,老师把学习成绩靠前的学生跟学习成绩靠后的学生排在一组.本题就请你编写程序帮助老师自动完成这个分配工作,即在得到全班学生的排名后,在当前尚未分组的学生中, ...