C#之任务,线程和同步

1 概述

  对于所有需要等待 的操作,例 如 ,因 为文件 、 数据库或网络访 问都需要一定 的时间,此 时就可以启 动一个新线程,同时完成其他任务,即使是处理密集型的任务,线程也是有帮助的。

2 Parallel类

  2.1 用Parallel.For()方法循环

  Parallel.For()方法类似于C#的For循环,多次执行一个任务,它可以并行运行迭代。迭代的顺序没有定义。

1 ParallelLoopResult result = Parallel.For(0, 10, i =>
2             {
3                 Console.WriteLine("{0},task:{1},thread:{2}", i, Task.CurrentId, Thread.CurrentThread.ManagedThreadId);
4                 Thread.Sleep(10);
5             });
6             Console.WriteLine(result.IsCompleted);

  在For()方法中,前两个参数定义了循环的开头和结束。从输出可以看出,顺序是不能保证的。也可以提前中断Parallel.For()方法。

1 ParallelLoopResult result2 = Parallel.For(10, 40, (int i,ParallelLoopState pls) =>
2             {
3                 Console.WriteLine("i: {0},task:{1}", i, Task.CurrentId);
4                 Thread.Sleep(10);
5                 if (i > 15)
6                     pls.Break();
7             });
8             Console.WriteLine(result2.IsCompleted);
9             Console.WriteLine( "lowest break iteration:{0}",result2.LowestBreakIteration);

  2.2 用Parallel.ForEach()方法循环

  paraller.ForEach()方法遍历实现了IEnumerable的集合,其方式类似于Foreach语句,但以异步方式遍历,这里也没有确定的遍历顺序。

1     string[] data = { "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "eleven", "twelve" };
2             //  Parallel.ForEach(data, s => { Console.WriteLine(s); });
3             Parallel.ForEach(data, (s, pls) => { if (s == "one") { Console.WriteLine("break......"); pls.Break(); } Console.WriteLine(s); Thread.Sleep(100); });

  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类 的构造函数在创建任务上提供的灵活性较大.

1    //using TaskFactory
2             Task t1 = new TaskFactory().StartNew(TaskMethod);
3             //using the task factory via task
4             Task t2 = Task.Factory.StartNew(TaskMethod);
5             //using task constructor
6             Task t3 = new Task(TaskMethod);
7             t3.Start();

使用 Task类 的构造函数和 TaskFactory类 的 stamw()方法时,都可以传递TaskCreationOptions枚举中的值。 设置LongRunning选项,可 以通知任务调度器,该 任务需要较长时间执行,这样调度器更可能使用 新线。 如果该任务应关联到父任务上,而父任务取消了,则 该任务也应取消,此 时应设置 AuachToParent选 项。PerferFairness 值表示,调度器应提取出已在等待的第一个任务。  如果任务使用 子任务创建了其他工作,子
任务就优先于其他任务。 它们不会排在线程池队列中的最后。 如果这些任务应 以公平的方式与所有其他任务一起处理,就设置该选项为PreferFairness

1   Task t5 = t4.ContinueWith(DoSecond,TaskContinuationOptions.PreferFairness);

  3.2连续的任务

  通过任务,可 以指定在任务完成后,应 开始运行另一个特定任务.

 1  static void DoOnFirst()
 2         {
 3             Console.WriteLine("doing some task {0}",Task.CurrentId);
 4             Thread.Sleep(3000);
 5         }
 6         static void DoSecond(Task t)
 7         {
 8             Console.WriteLine("task {0} finished",t.Id);
 9             Console.WriteLine("this task id {0}",Task.CurrentId);
10             Console.WriteLine("do some cleanup");
11             Thread.Sleep(3000);
12         }
13
14  Task t1 = new Task(DoOnFirst);
15             Task t2 = t1.ContinueWith(DoSecond);
16             Task t3 = t2.ContinueWith(DoSecond);
17             Task t4 = t3.ContinueWith(DoSecond);
18             Task t5 = t4.ContinueWith(DoSecond,TaskContinuationOptions.PreferFairness);
19             t1.Start();

无论前一个任务是如何结束的,前 面 的连续任务总是在前一个任务结束时启 动 。 使用TaskContinuationOptions 枚举中的值,可 以指定,连续任务只有在起始任务成功(或失败)结束时启动。

  3.3任务层次的结构

 1  static void ParentAndChild()
 2         {
 3             var parent = new Task(ParentTask);
 4             parent.Start();
 5             Thread.Sleep(2000);
 6             Console.WriteLine(parent.Status);
 7             Thread.Sleep(4000);
 8             Console.WriteLine(parent.Status);
 9             Console.WriteLine();
10         }
11         private static void ParentTask()
12         {
13             Console.WriteLine("task id {0}",Task.CurrentId);
14             var child = new Task(ChildTask);
15             child.Start();
16             Thread.Sleep(1000);
17             Console.WriteLine("parent started child");
18         }
19
20         private static void ChildTask()
21         {
22             Console.WriteLine("child");
23             Thread.Sleep(5000);
24             Console.WriteLine("child finished");
25         }

如果父任务在子任务之前结束 ,父 任务的状态就显示为WaitingForChildrenToComplete.只要子任务也结束 时,父任务的状态就变成RanToCompletion。 ·

4 取消架构

  4.1Parallel.For()方法的取消

 1 var cts = new CancellationTokenSource();
 2             cts.Token.Register(() => Console.WriteLine("token canceled"));
 3             new Task(() => { Thread.Sleep(500); cts.Cancel(false); }).Start();
 4             try
 5             {
 6                 ParallelLoopResult result = Parallel.For(0, 100, new ParallelOptions() { CancellationToken = cts.Token, }, x =>
 7                 {
 8                     Console.WriteLine("loop {0} started", x);
 9                     int sun = 0;
10                     for (int i = 0; i < 100; i++)
11                     {
12                         Thread.Sleep(2);
13                         sun += i;
14                     }
15                     Console.WriteLine("loop {0} finished",x);
16                 });
17             }
18             catch (Exception ex)
19             {
20                 Console.WriteLine(ex.Message);
21             }

  4.2任务的取消

  同样的取消模式也可用于任务。

5 线程池

  如果有不同的小任务要完成,就可以事先创建许多线程 ,· 在应完成这些任务时发出请求。 这个线程数最好在需要更多的线程时增加,在 需要释放资源时减少。不需要自己创建这样一个列表。 该列表由 ThreadPool类 托管。 这个类会在需要时增减池中线程的线程数,直 到最大的线程数。 池中的最大线程数是可配置的。如果有更多的作业要处理,线 程池中线程的个数也到了极限,最 新的作业就要排队,且 必须等待线程完成其任务。

 1  static void Main(string[] args)
 2         {
 3             int nWorkerThreads;
 4             int nCompletionPortThreads;
 5             ThreadPool.GetMaxThreads(out nWorkerThreads, out nCompletionPortThreads);
 6             Console.WriteLine("nWorkerThreads:{0},nCompletionPortThreads:{1}", nWorkerThreads, nCompletionPortThreads);
 7
 8             for (int i = 0; i < 5; i++)
 9             {
10                 ThreadPool.QueueUserWorkItem(JobForAThread);
11             }
12             Thread.Sleep(3000);
13             Console.ReadKey();
14         }
15         static void JobForAThread(object obj)
16         {
17             for (int i = 0; i < 3; i++)
18             {
19                 Console.WriteLine("loop:{0},running inside pooled thread{1}",i,Thread.CurrentThread.ManagedThreadId);
20                 Thread.Sleep(30);
21             }
22         }

  线程池使用起来很简单,但 它有一些限制 :

  • 线程池 中 的所有线程都是后 台线程 。 如 果进程 的所有前 台线程都结束 了,所 有 的后 台线程就会停止 。 不能把入池的线程改为前台线程 。
  • 不 能给入池的线程设置优先级或名 称 。
  • 入池的线程只能用 于时间较短的任务 。 如 果线程要一直运行(如 Word的 拼写检查器线程),就应使用 Therd类创 建一个线程

6 Therd类

使用Thread类可以创建和控制线程,

1 new Thread(() => { Console.WriteLine("Running in thread"); }).Start();
2             Console.WriteLine("this is the main thread");

  6.1给后台线程传递数据

  给线程传递一些数据可以采用2中方式,一种是使用带ParameterizdThreadStart委托参数的Thread构造函数,另一种方式是常见一个自定义的类,把线程的方法定义为实例方法。

  6.2后台任务

  只要有一个前台相称在运行,程序的进程就在运行,如果前台多个线程在运行,而Main()方法结束了,应用程序的进程直到所有前台完成其任务前都处于激活状态。默认情况下,用Thread创建的线程为前台线程,线程池中的线程为总是为后台线程。Thread类可以设置IsBackground属性设置是否为前台线程。

 1 static void Main(string[] args)
 2         {
 3             var t1 = new Thread(ThreadMain) { Name = "NewThread", IsBackground = false };
 4             t1.Start();
 5             Console.WriteLine("Main thread ending now");
 6             Console.ReadKey();
 7         }
 8
 9
10         static void ThreadMain()
11         {
12             Console.WriteLine("Thread {0} statrted",Thread.CurrentThread.Name);
13             Thread.Sleep(5000);
14             Console.WriteLine("Thread {0} completed",Thread.CurrentThread.Name);
15         }

  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语 句是设置锁定和解除锁定的一种简单方式。

 1  static void Main()
 2         {
 3             int numTask = 20;
 4             var state = new ShareState();
 5             var tasks = new Task[numTask];
 6             for (int i = 0; i < numTask; i++)
 7             {
 8                 tasks[i] = new Task(new Job(state).DoWork);
 9                 tasks[i].Start();
10             }
11
12             for (int i = 0; i < numTask; i++)
13             {
14                 tasks[i].Wait();
15             }
16             Console.WriteLine("Sun :{0}",state.State);
17             Console.ReadKey();
18         }
19     }
20     public class Job
21     {
22         ShareState shareState;
23         public Job(ShareState shareState)
24         {
25             this.shareState = shareState;
26         }
27         public void DoWork()
28         {
29             for (int i = 0; i < 5000; i++)
30             {
31                 shareState.State += 1;
32             }
33         }
34     }
35     public class ShareState
36     {
37         public int State { get; set; }
38     }

上面的代码,因为执行了5000次循环,有20个任务,所以输出的值应为100000,但是,事实并非如此。使用Lock修改DoWork方法

1   public void DoWork()
2         {
3             for (int i = 0; i < 5000; i++)
4             {
5                 lock (shareState)
6                 shareState.State += 1;
7             }
8         }

这样结果总是正确的。但是在一个地方使用Lock语句并不意味着,访问对象的其他线程都在等待,必须对每个访问共享状态的线程显示的使用同步功能。继续需改

 1   static void Main()
 2         {
 3             int numTask = 20;
 4             var state = new ShareState();
 5             var tasks = new Task[numTask];
 6             for (int i = 0; i < numTask; i++)
 7             {
 8                 tasks[i] = new Task(new Job(state).DoWork);
 9                 tasks[i].Start();
10             }
11
12             for (int i = 0; i < numTask; i++)
13             {
14                 tasks[i].Wait();
15             }
16             Console.WriteLine("Sun :{0}", state.State);
17             Console.ReadKey();
18         }
19     }
20     public class Job
21     {
22         ShareState shareState;
23         public Job(ShareState shareState)
24         {
25             this.shareState = shareState;
26         }
27         public void DoWork()
28         {
29             for (int i = 0; i < 5000; i++)
30             {
31                 shareState.IncrementState();
32             }
33         }
34     }
35     public class ShareState
36     {
37         private int state = 0;
38         private object obj = new object();
39         public int State
40         {
41             get
42             {
43                 return state;
44             }
45         }
46         public int IncrementState()
47         {
48             lock(obj)
49                 return ++state;
50         }
51     }

  8.2 Interlocked类

  Ihterlockcd类用 于使变量的简单语旬原子化。 i++不是线程安全的,它 的操作包括从内存中获取一个值,给该值递增 1,再 将它存储回内存。 这些操作都可能会被线程调度器打断。 Ihterlocked类提供了以线程安全的方式递增、 递减、‘交换和读取值的方法。 与其他同步技术相 比,使用 Ihterlocked类 会快得多。 但是,它 只能用于简单的同步问题。

  8.3 Monitor类

  C#的lock语 句 ,由编译器解析为使用monitor类,与C#的 lock语 句相 比,Monitor 类的主要优点是:可 以添加一个等待被锁定的超时值 。 这样就不会无限期地等待被锁定.

 1 object obj = new object();
 2             bool lockTaken = false;
 3             Monitor.TryEnter(obj, 500, ref lockTaken);
 4             if (lockTaken)
 5             {
 6                 try
 7                 {
 8                     //已经锁定,想干嘛就干嘛吧
 9                 }
10                 finally
11                 {
12                     Monitor.Exit(obj);
13                 }
14             }
15             else
16             {
17                 //没有锁定,小心喽
18             }

  8.4 Mutex类

  Mutex【Mutual exclusion ,互 斥)是.Net Freamwork中 提供跨多个进程同步访问的一个类 由于系统能识别有名称的互斥,因 此可 以使用 它禁止应用程序启动两次

 1   static class Program
 2     {
 3         /// <summary>
 4         /// 应用程序的主入口点。
 5         /// </summary>
 6         [STAThread]
 7         static void Main()
 8         {
 9             bool createNew;
10
11             Mutex m = new Mutex(false, "test", out createNew);
12             if (!createNew)
13             {
14                 MessageBox.Show("程序已启动");
15                 Application.Exit();
16                 return;
17             }
18             Application.EnableVisualStyles();
19             Application.SetCompatibleTextRenderingDefault(false);
20             Application.Run(new Form1());
21         }
22     }

时间: 2024-10-15 21:22:25

C#之任务,线程和同步的相关文章

Linux环境下线程消息同步的陷阱

我们程序中常常会使用到线程间的消息同步处理,比如以下一段伪码 var message = "": void func()  {   1. 启动线程Thread(该线程中填充message的内容):   2. 阻塞,直到等待到完成message填充的事件:   3. 处理message:   .... } void Thread()  {   1. 通过某种处理填充message:   2. 触发func中的阻塞事件: } 我们通常会使用条件变量来完成类似情况的线程同步处理 比如wind

线程+任务+同步

线程: 对于所有需要等待的操作,例如移动文件,数据库和网络访问都需要一定的时间,此时就可以启动一个新的线程,同时完成其他任务.一个进程的多个线程可以同时运行在不同的CPU上或多核CPU的不同内核上. 线程是程序中独立的指令流.在VS编辑器中输入代码的时候,系统会分析代码,用下划线标注遗漏的分号和其他语法错误,这就是用一个后台线程完成.Word文档需要一个线程等待用户输入,另一个线程进行后台搜索,第三个线程将写入的数据存储在临时文件中.运行在服务器上的应用程序中等待客户请求的线程成为侦听器线程.

线程间同步之信号量实现环形buf

一.概述: 信号量是一个非负整数的计数器,它通过计数器来实现多线程对临界资源的顺序访问,从而实现线程间的同步.它与进程间通信的信号量不同,进程间通信的信号量是一个信号量集,而线程间同步的信号量是一个信号.还有一点,就是对信号量的操作是原子的. 信号量与互斥锁的区别: (1).互斥锁的值只能是0或1,而信号量的值为非负整数. (2).互斥锁用与实现线程间的互斥,而信号量用于实现线程间的同步. (3).互斥锁的加锁和解锁必须由同一个线程分别对应使用,而信号量可以由一个线程得到,另一个线程释放. 下面

线程的同步与互斥,死锁

线程的同步与互斥 多个线程同时访问共享数据时可能会发生冲突,比如两个线程同时把一个全局变量加1,结果可能不是我们所期待的: 我们看这段代码的执行结果: #include <stdio.h> #include <stdlib.h> #include <pthread.h> static int g_count=0; void *thread(void *arg) { int index=0; int tmp=0; while(index++<5000) { tmp=

线程和同步

线程和同步 1 概述 对于所有需要等待 的操作,例 如 ,因 为文件 . 数据库或网络访 问都需要一定 的时间,此 时就可以启 动一个新线程,同时完成其他任务,即使是处理密集型的任务,线程也是有帮助的. 2 Parallel类 2.1 用Parallel.For()方法循环 Parallel.For()方法类似于C#的For循环,多次执行一个任务,它可以并行运行迭代.迭代的顺序没有定义. 1 ParallelLoopResult result = Parallel.For(0, 10, i =>

第二十八(线程的同步、守护线程、Timer 定时器)

/* 模拟ATM取款,不是线程同步机制,多线程同时对一个账户进行操作 t1 和 t2 异步编程模型:t1线程执行t1,t2线程执行的是t2,两个线程之间谁也不等于谁 同步编程模型:t1线程和t2线程执行,当t1线程必须等于t2的线程执行结果之后,t1线程才能执行 这是同步编程模型. 什么时候需要引入同步 1. 为了数据安全,尽管应用程序的使用率低,但是为了保证数据安全性,必须的加入线程同步机制 线程同步机制 使程序变成了(等同)单线程 2. 在什么条件下需要使用线程同步 <1> 必须是多线程环

Java线程:线程的同步与锁

一.同步问题提出 线程的同步是为了防止多个线程访问一个数据对象时,对数据造成的破坏. 例如:两个线程ThreadA.ThreadB都操作同一个对象Foo对象,并修改Foo对象上的数据. public class Foo {     private int x = 100; public int getX() {         return x;     } public int fix(int y) {         x = x - y;         return x;     } }

线程的同步synchronized

一个Java程序的多线程之间可以共享数据. 当线程以异步方式访问共享数据时,有时候是不安全的或者不和逻辑的.比如卖火车票,同一时刻一个线程在读取数据,另外一个线程在处理数据,当处理数据的线程没有等到读取数据的线程读取完毕就去处理数据,必然得到错误的处理结果. 卖火车票Demo: class MyThread implements Runnable{ private int ticket = 5 ; // 假设一共有5张票 public void run(){ for(int i=0;i<100;

Java并发编程:线程的同步

.title { text-align: center } .todo { font-family: monospace; color: red } .done { color: green } .tag { background-color: #eee; font-family: monospace; padding: 2px; font-size: 80%; font-weight: normal } .timestamp { color: #bebebe } .timestamp-kwd

Java线程:线程的同步与锁

1.同步和锁 java中每个对象都有一个内置锁,程序执行非静态的synchronized同步方法时,自动获得执行该方法的对象有关的锁. 一个对象只有一个锁,当一个线程获得锁,其他线程不能进入该对象上的synchronized方法或代码块.直到锁被释放. 线程可以有多个锁,例如,一个对象的同步方法中调用另一个对象的同步方法 2.静态方法的同步 同步静态方法,用的是类对象的锁,即xx.class. 3.线程安全类 对类中的方法进行了同步,但操作这个类时仍然不一定安全. 例如: public  cla