C#之任务,线程和同步
1 概述
对于所有需要等待 的操作,例 如 ,因 为文件 、 数据库或网络访 问都需要一定 的时间,此 时就可以启 动一个新线程,同时完成其他任务,即使是处理密集型的任务,线程也是有帮助的。
2 Parallel类
2.1 用Parallel.For()方法循环
Parallel.For()方法类似于C#的For循环,多次执行一个任务,它可以并行运行迭代。迭代的顺序没有定义。
ParallelLoopResult result = Parallel.For(, , i => { Console.WriteLine("{0},task:{1},thread:{2}", i, Task.CurrentId, Thread.CurrentThread.ManagedThreadId); Thread.Sleep(); }); Console.WriteLine(result.IsCompleted);
在For()方法中,前两个参数定义了循环的开头和结束。从输出可以看出,顺序是不能保证的。也可以提前中断Parallel.For()方法。
ParallelLoopResult result2 = Parallel.For(, , (int i,ParallelLoopState pls) => { Console.WriteLine("i: {0},task:{1}", i, Task.CurrentId); Thread.Sleep(); ) pls.Break(); }); Console.WriteLine(result2.IsCompleted); Console.WriteLine( "lowest break iteration:{0}",result2.LowestBreakIteration);
2.2 用Parallel.ForEach()方法循环
paraller.ForEach()方法遍历实现了IEnumerable的集合,其方式类似于Foreach语句,但以异步方式遍历,这里也没有确定的遍历顺序。
string[] data = { "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "eleven", "twelve" }; // Parallel.ForEach(data, s => { Console.WriteLine(s); }); Parallel.ForEach(data, (s, pls) => { ); });
2.3 通过Paraller.Invoke()调用多个方法
Parallel.Invoke(Foo ,Bar); static void Foo() { Console.WriteLine("foo"); } static void Bar() { Console.WriteLine("bar"); }
3 任务
.NET 4 包含新的名称空间System.Threading.Task,它它 包含的类抽象出了线程功能,在后台使用ThreadPool。 任务表示应完成的某个单元的工作。 这个单元的工作可以在单独的线程中运行,也可以以同步方式启动一个任务,这需要等待主调线程。
3.1启动任务
要启动任务,可 以使用 TaskFactory类 或 Task类 的构造函数和 start()方 法。 Task类 的构造函数在创建任务上提供的灵活性较大.
//using TaskFactory Task t1 = new TaskFactory().StartNew(TaskMethod); //using the task factory via task Task t2 = Task.Factory.StartNew(TaskMethod); //using task constructor Task t3 = new Task(TaskMethod); t3.Start();
使用 Task类 的构造函数和 TaskFactory类 的 stamw()方法时,都可以传递TaskCreationOptions枚举中的值。 设置LongRunning选项,可 以通知任务调度器,该 任务需要较长时间执行,这样调度器更可能使用 新线。 如果该任务应关联到父任务上,而父任务取消了,则 该任务也应取消,此 时应设置 AuachToParent选 项。PerferFairness 值表示,调度器应提取出已在等待的第一个任务。 如果任务使用 子任务创建了其他工作,子
任务就优先于其他任务。 它们不会排在线程池队列中的最后。 如果这些任务应 以公平的方式与所有其他任务一起处理,就设置该选项为PreferFairness
Task t5 = t4.ContinueWith(DoSecond,TaskContinuationOptions.PreferFairness);
3.2连续的任务
通过任务,可 以指定在任务完成后,应 开始运行另一个特定任务.
static void DoOnFirst() { Console.WriteLine("doing some task {0}",Task.CurrentId); Thread.Sleep(); } static void DoSecond(Task t) { Console.WriteLine("task {0} finished",t.Id); Console.WriteLine("this task id {0}",Task.CurrentId); Console.WriteLine("do some cleanup"); Thread.Sleep(); } Task t1 = new Task(DoOnFirst); Task t2 = t1.ContinueWith(DoSecond); Task t3 = t2.ContinueWith(DoSecond); Task t4 = t3.ContinueWith(DoSecond); Task t5 = t4.ContinueWith(DoSecond,TaskContinuationOptions.PreferFairness); t1.Start();
无论前一个任务是如何结束的,前 面 的连续任务总是在前一个任务结束时启 动 。 使用TaskContinuationOptions 枚举中的值,可 以指定,连续任务只有在起始任务成功(或失败)结束时启动。
3.3任务层次的结构
static void ParentAndChild() { var parent = new Task(ParentTask); parent.Start(); Thread.Sleep(); Console.WriteLine(parent.Status); Thread.Sleep(); Console.WriteLine(parent.Status); Console.WriteLine(); } private static void ParentTask() { Console.WriteLine("task id {0}",Task.CurrentId); var child = new Task(ChildTask); child.Start(); Thread.Sleep(); Console.WriteLine("parent started child"); } private static void ChildTask() { Console.WriteLine("child"); Thread.Sleep(); Console.WriteLine("child finished"); }
如果父任务在子任务之前结束 ,父 任务的状态就显示为WaitingForChildrenToComplete.只要子任务也结束 时,父任务的状态就变成RanToCompletion。 ·
4 取消架构
4.1Parallel.For()方法的取消
var cts = new CancellationTokenSource(); cts.Token.Register(() => Console.WriteLine("token canceled")); ); cts.Cancel(false); }).Start(); try { ParallelLoopResult result = Parallel.For(, , new ParallelOptions() { CancellationToken = cts.Token, }, x => { Console.WriteLine("loop {0} started", x); ; ; i < ; i++) { Thread.Sleep(); sun += i; } Console.WriteLine("loop {0} finished",x); }); } catch (Exception ex) { Console.WriteLine(ex.Message); }
4.2任务的取消
同样的取消模式也可用于任务。
5 线程池
如果有不同的小任务要完成,就可以事先创建许多线程 ,· 在应完成这些任务时发出请求。 这个线程数最好在需要更多的线程时增加,在 需要释放资源时减少。不需要自己创建这样一个列表。 该列表由 ThreadPool类 托管。 这个类会在需要时增减池中线程的线程数,直 到最大的线程数。 池中的最大线程数是可配置的。如果有更多的作业要处理,线 程池中线程的个数也到了极限,最 新的作业就要排队,且 必须等待线程完成其任务。
static void Main(string[] args) { int nWorkerThreads; int nCompletionPortThreads; ThreadPool.GetMaxThreads(out nWorkerThreads, out nCompletionPortThreads); Console.WriteLine("nWorkerThreads:{0},nCompletionPortThreads:{1}", nWorkerThreads, nCompletionPortThreads); ; i < ; i++) { ThreadPool.QueueUserWorkItem(JobForAThread); } Thread.Sleep(); Console.ReadKey(); } static void JobForAThread(object obj) { ; i < ; i++) { Console.WriteLine("loop:{0},running inside pooled thread{1}",i,Thread.CurrentThread.ManagedThreadId); Thread.Sleep(); } }
线程池使用起来很简单,但 它有一些限制 :
- 线程池 中 的所有线程都是后 台线程 。 如 果进程 的所有前 台线程都结束 了,所 有 的后 台线程就会停止 。 不能把入池的线程改为前台线程 。
- 不 能给入池的线程设置优先级或名 称 。
- 入池的线程只能用 于时间较短的任务 。 如 果线程要一直运行(如 Word的 拼写检查器线程),就应使用 Therd类创 建一个线程
6 Therd类
使用Thread类可以创建和控制线程,
new Thread(() => { Console.WriteLine("Running in thread"); }).Start(); Console.WriteLine("this is the main thread");
6.1给后台线程传递数据
给线程传递一些数据可以采用2中方式,一种是使用带ParameterizdThreadStart委托参数的Thread构造函数,另一种方式是常见一个自定义的类,把线程的方法定义为实例方法。
6.2后台任务
只要有一个前台相称在运行,程序的进程就在运行,如果前台多个线程在运行,而Main()方法结束了,应用程序的进程直到所有前台完成其任务前都处于激活状态。默认情况下,用Thread创建的线程为前台线程,线程池中的线程为总是为后台线程。Thread类可以设置IsBackground属性设置是否为前台线程。
static void Main(string[] args) { var t1 = new Thread(ThreadMain) { Name = "NewThread", IsBackground = false }; t1.Start(); Console.WriteLine("Main thread ending now"); Console.ReadKey(); } static void ThreadMain() { Console.WriteLine("Thread {0} statrted",Thread.CurrentThread.Name); Thread.Sleep(); Console.WriteLine("Thread {0} completed",Thread.CurrentThread.Name); }
6.3线程的优先级
线 程曲操作系统调度。 给线程指定优先级,就 可 以影响调度顺序。在Thread类中,可以设置Priority属性设置线程的优先级,Priority属性需要ThreadPriority枚举定义的一个值,定义级别有Highest,AboveNormal,Normal,BelowNormal和Lowest。
6.4控制线程
调用 Thread对 象的Start()方 法,可 以创建线程。 但是,在 调用Strat()方法后,新线程仍不是处于 Running状态,而 是处于 Unstarted状 态。 只要操作系统的线程调度器选择了要运行的线程,线程就会改为Running状态 。 读取Thread.ThreadState属 性,就可以获得线程的当前状态。使用 Thread.Sleep() 方法 ,会使线程处于WaitSleepJoin状态,在 经历Sleep()方法定义的时间段后 ,线程就会等待再次被唤醒。要停止另一个线程,可 以调用Thread.Abort()方 法。 调用这个方法时,会 在接到终止命令的线程中抛出一个ThreadAbortException类 型的异常。 用一个处理程序捕获这个异常,线程可 以在结束前完成一些清理工作。如 果需要等待线程的结束,就 可 以调用Thread.Join()方 法 。此方 法会停止当前线程 ,并把它设置为WaitSleepJoin状 态 ,直 到加入 的线程完成为止 。
7线程问题
7.1争用条件
如果两个或多个线程访问相同的对象,或 者访问不同步的共享状态,就会出现争用条件。
7.2死锁
过多的锁定也会有麻烦。 在死锁中,至少有两个线程被挂起,并等待对方解除锁定。 由于两个线程都在等待对方,就 出现了死锁,线程将无限等待下去。
8 同步
8.1 Lock语句和线程安全
C#为多个线程的同步提供了 自己的关键字:lock语 句 。 lock语 句是设置锁定和解除锁定的一种简单方式。
static void Main() { ; var state = new ShareState(); var tasks = new Task[numTask]; ; i < numTask; i++) { tasks[i] = new Task(new Job(state).DoWork); tasks[i].Start(); } ; i < numTask; i++) { tasks[i].Wait(); } Console.WriteLine("Sun :{0}",state.State); Console.ReadKey(); } } public class Job { ShareState shareState; public Job(ShareState shareState) { this.shareState = shareState; } public void DoWork() { ; i < ; i++) { shareState.State += ; } } } public class ShareState { public int State { get; set; } }
上面的代码,因为执行了5000次循环,有20个任务,所以输出的值应为100000,但是,事实并非如此。使用Lock修改DoWork方法
public void DoWork() { ; i < ; i++) { lock (shareState) shareState.State += ; } }
这样结果总是正确的。但是在一个地方使用Lock语句并不意味着,访问对象的其他线程都在等待,必须对每个访问共享状态的线程显示的使用同步功能。继续需改
static void Main() { ; var state = new ShareState(); var tasks = new Task[numTask]; ; i < numTask; i++) { tasks[i] = new Task(new Job(state).DoWork); tasks[i].Start(); } ; i < numTask; i++) { tasks[i].Wait(); } Console.WriteLine("Sun :{0}", state.State); Console.ReadKey(); } } public class Job { ShareState shareState; public Job(ShareState shareState) { this.shareState = shareState; } public void DoWork() { ; i < ; i++) { shareState.IncrementState(); } } } public class ShareState { ; private object obj = new object(); public int State { get { return state; } } public int IncrementState() { lock(obj) return ++state; } }
8.2 Interlocked类
Ihterlockcd类用 于使变量的简单语旬原子化。 i++不是线程安全的,它 的操作包括从内存中获取一个值,给该值递增 1,再 将它存储回内存。 这些操作都可能会被线程调度器打断。 Ihterlocked类提供了以线程安全的方式递增、 递减、'交换和读取值的方法。 与其他同步技术相 比,使用 Ihterlocked类 会快得多。 但是,它 只能用于简单的同步问题。
8.3 Monitor类
C#的lock语 句 ,由编译器解析为使用monitor类,与C#的 lock语 句相 比,Monitor 类的主要优点是:可 以添加一个等待被锁定的超时值 。 这样就不会无限期地等待被锁定.
object obj = new object(); bool lockTaken = false; Monitor.TryEnter(obj, , ref lockTaken); if (lockTaken) { try { //已经锁定,想干嘛就干嘛吧 } finally { Monitor.Exit(obj); } } else { //没有锁定,小心喽 }
8.4 Mutex类
Mutex【Mutual exclusion ,互 斥)是.Net Freamwork中 提供跨多个进程同步访问的一个类 由于系统能识别有名称的互斥,因 此可 以使用 它禁止应用程序启动两次
static class Program { /// <summary> /// 应用程序的主入口点。 /// </summary> [STAThread] static void Main() { bool createNew; Mutex m = new Mutex(false, "test", out createNew); if (!createNew) { MessageBox.Show("程序已启动"); Application.Exit(); return; } Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new Form1()); } }
C#之任务,线程和同步的更多相关文章
- Java线程:线程的同步-同步方法
Java线程:线程的同步-同步方法 线程的同步是保证多线程安全访问竞争资源的一种手段. 线程的同步是Java多线程编程的难点,往往开发者搞不清楚什么是竞争资源.什么时候需要考虑同步,怎么同步等等问 ...
- Java线程:线程的同步与锁
一.同步问题提出 线程的同步是为了防止多个线程访问一个数据对象时,对数据造成的破坏. 例如:两个线程ThreadA.ThreadB都操作同一个对象Foo对象,并修改Foo对象上的数据. public ...
- java 线程数据同步
java 线程数据同步 由买票实例 //java线程实例 //线程数据同步 //卖票问题 //避免重复卖票 //线程 class xc1 implements Runnable{ //定义为静态,可以 ...
- Java多线程-线程的同步与锁
一.同步问题提出 线程的同步是为了防止多个线程访问一个数据对象时,对数据造成的破坏.例如:两个线程ThreadA.ThreadB都操作同一个对象Foo对象,并修改Foo对象上的数据. package ...
- C++ 11 线程的同步与互斥
这次写的线程的同步与互斥,不依赖于任何系统,完全使用了C++11标准的新特性来写的,就连线程函数都用了C++11标准的lambda表达式. /* * thread_test.cpp * * Copyr ...
- C#线程间同步无法关闭
用C#做了个线程间同步的小程序,但每次关闭窗口后进程仍然在,是什么原因? 解决方法: 要加一句 线程.IsBackground = true; 否则退出的只是窗体 上面的方法没看懂... MSDN上说 ...
- 线程间同步之 semaphore(信号量)
原文地址:http://www.cnblogs.com/yuqilin/archive/2011/10/16/2214429.html semaphore 可用于进程间同步也可用于同一个进程间的线程同 ...
- Linux系统编程(29)——线程间同步(续篇)
线程间的同步还有这样一种情况:线程A需要等某个条件成立才能继续往下执行,现在这个条件不成立,线程A就阻塞等待,而线程B在执行过程中使这个条件成立了,就唤醒线程A继续执行.在pthread库中通过条件变 ...
- linux线程间同步方式汇总
抽空做了下linux所有线程间同步方式的汇总(原生的),包含以下几个: 1, mutex 2, condition variable 3, reader-writer lock 4, spin loc ...
随机推荐
- [置顶] java Gui 键盘监听事件
简单写一个java Gui键盘监听事件,实现的效果就是按下键盘控制台输出你按下的键.比如:按下A控制台就输出A 效果如图: 以下把实现的效果分为几个步骤: 1.新建一个窗体类继承窗体: 2.给这个窗体 ...
- Cocos2dx 3.0 过渡篇(三十一)ValueVector和Vector不得不说的故事
本文投票地址:http://vote.blog.csdn.net/Article/Details?articleid=37834689 前天看到一个颇为纠结的选择题:有一天你遇到一个外星人,这时外星人 ...
- Logistic Regression(逻辑回归)(一)基本原理
(整理自AndrewNG的课件,转载请注明.整理者:华科小涛@http://www.cnblogs.com/hust-ghtao/) 虽然叫做“回归”,但是这个算法是用来解决分类问题的.回归与分类的区 ...
- CButtonEx的实现
要想修改CButton类按钮背景颜色和文字颜色,必须利用自绘方法对按钮进行重新绘制.这可以通过定义一个以CButton为基类的新按钮类来实现.以下为具体的实现方法: 方法一: 加入一个新类,类名:CB ...
- 实现StatusBar的Flat风格
效果见右图,OfficeXP里就是这样的风格,其实实现很简单,不必专门在网上找别人控件. 把StatusBar的SimplePanel设为False,点击Panels添加StatusPanel,把所有 ...
- OpenVPN多处理之-netns容器与iptables CLUSTER
假设还是沉湎于之前的战果以及强加的感叹,不要冥想,将其升华. 1.C还是脚本 以前,我用bash组织了复杂的iptables,ip rule等逻辑来配合OpenVPN,将其应用于差点儿全部能够想象得到 ...
- Swift - 几种使用数组的数据存储模型
在iOS游戏开发中,比如2048游戏.有时会需要存储N×N数组的数据模型(如3×3,4×4等).这里我们演示了三种实现方式,分别是:一维数组.仿二维数组.自定义二维数组(即矩阵结构). 功能是根据传入 ...
- 积累的VC编程小技巧之列表框
1.列表框中标题栏(Column)的添加 创建一个List Control,其ID为IDC_LIST,在其Styles属性项下的View项里选择Report.Align项里选择Top.Sort项里选择 ...
- CAS (1) —— Mac下配置CAS到Tomcat(服务端)(转)
tomcat版本: tomcat-8.0.29 jdk版本: jdk1.8.0_65 cas版本: cas4.1.2cas-client-3.4.1 参考来源: CAS实现单点登录(SSO)经典完整教 ...
- POJ 2442 Squence (STL heap)
题意: 给你n*m的矩阵,然后每行取一个元素,组成一个包含n个元素的序列,一共有n^m种序列, 让你求出序列和最小的前n个序列的序列和. 解题思路: 1.将第一序列读入seq1向量中,并按升序排序. ...