构建多线程应用程序(Thread、lock锁定范围、Monitor、Interlocked、[Synchronization]、TimerCallback、ThreadPool线程池)
2014-01-04 11:43
441 查看
一、System.Threading.Thread类
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Linq.Expressions; using System.Collections.Specialized; using System.Reflection; using System.Data.Linq.Mapping; using System.Reflection.Emit; using System.Threading; namespace ConsoleApplication1 { class Program { static void Main(string[] args) { Thread t = Thread.CurrentThread;//获得当前正在运行的线程 t.Name = "ThisPrimaryThread";//设置线程名称 Console.WriteLine("当前线程正在运行的应用程序域: "+Thread.GetDomain().FriendlyName); Console.WriteLine("当前线程正在执行的上下文:" + Thread.CurrentContext.ContextID); Console.WriteLine("线程名称: " + t.Name); Console.WriteLine("当前线程执行状态: " + t.IsAlive); Console.WriteLine("当前线程优先级别: " + t.Priority); Console.WriteLine("当前线程的状态: "+t.ThreadState); Console.ReadKey(); } } }
二、创建次线程
//ThreadStart 线程开始执行时,调用无参方法Thread backgroundThread = new Thread(
new ThreadStart(无参方法名) );
backgroundThread.Name = "Sencondary";
backgroundThread.Start(); //通知CLR,尽快执行本线程
//ParameterizedThreadStart 线程开始执行时,调用带参方法
Thread backgroundThread2 = new Thread(
new ParameterizedThreadStart(带参方法名) );
backgroundThread2.Name = "Sencondary2";
backgroundThread2.Start(ap);
代码示例一:
class Program { static void Main(string[] args) { Thread t = new Thread(new ThreadStart(Add)); t.Start(); Thread t2 = new Thread(new ParameterizedThreadStart(Add2)); t2.Start("带参方法"); Console.ReadKey(); } static void Add() { Console.WriteLine("无参方法"); } static void Add2(object obj) { Console.WriteLine(obj.ToString()); } }
代码示例二:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Windows.Forms; namespace ConsoleApplication1 { class Program { static void Main(string[] args) { Console.WriteLine("*******次线程******"); Console.WriteLine(""); Console.WriteLine("你想用 [1]或[2]线程?请输入1或2"); string threadCount = Console.ReadLine(); //命名当前线程 Thread primaryThread = Thread.CurrentThread; primaryThread.Name = "Primary"; //显示线程信息 Console.WriteLine("线程:{0} 正在执行主线程。",primaryThread.Name); //创建任务类 Printer p = new Printer(); AddParams ap = new AddParams(10,20); switch (threadCount) { case "2": //创建次线程 Thread backgroundThread = new Thread(new ThreadStart(p.PrintNumber));//ThreadStart 线程开始执行时,调用无参方法 backgroundThread.Name = "Sencondary"; backgroundThread.Start();//通知CLR,尽快执行本线程 Thread backgroundThread2 = new Thread(new ParameterizedThreadStart(Add));//ParameterizedThreadStart 线程开始执行时,调用带参方法 backgroundThread2.Name = "Sencondary2"; backgroundThread2.Start(ap); break; case "1": p.PrintNumber(); Add(ap); break; default: goto case "1"; } MessageBox.Show("忙碌中","主线程正在工作中....."); Console.ReadLine(); } static void Add(object data) { if (data is AddParams) { Console.WriteLine("线程ID:{0},运行Add。",Thread.CurrentThread.ManagedThreadId); AddParams ap=(AddParams)data; Console.WriteLine(" {0} + {1} = {2}",ap.a,ap.b,ap.a+ap.b); } } } public class Printer { public void PrintNumber() { Console.WriteLine("线程:{0},正在执行PrintNumber()。",Thread.CurrentThread.Name); Console.Write("数字:"); for (int i = 0; i < 9; i++) { Console.Write("{0},",i); Thread.Sleep(2000); } Console.WriteLine(""); } } class AddParams { public int a, b; public AddParams(int numb1, int numb2) { a = numb1; b = numb2; } } }
private static AutoResetEvent waintHandle = new AutoResetEvent(false); //强制线程等待
waintHandle.WaitOne();//阻止当前线程
waintHandle.Set();//通知其他线程,该线程已结束
namespace ConsoleApplication1 { class Program { private static AutoResetEvent waintHandle = new AutoResetEvent(false);//强制线程等待 static void Main(string[] args) { //创建任务类 AddParams ap = new AddParams(10,20); //创建次线程 Thread backgroundThread2 = new Thread(new ParameterizedThreadStart(Add)); backgroundThread2.Name = "Sencondary2"; backgroundThread2.Start(ap); waintHandle.WaitOne();//阻止当前线程 Console.WriteLine("其他线程在工作!"); Console.ReadLine(); } static void Add(object data) { if (data is AddParams) { Console.WriteLine("线程ID:{0},运行Add。",Thread.CurrentThread.ManagedThreadId); AddParams ap=(AddParams)data; Console.WriteLine(" {0} + {1} = {2}",ap.a,ap.b,ap.a+ap.b); waintHandle.Set();//通知其他线程,该线程已结束 } } } class AddParams { public int a, b; public AddParams(int numb1, int numb2) { a = numb1; b = numb2; } } }
Net的公用语言运行时(CLR)能区分两种不同类型的线程:前台线程(Thread.Start()方法创建的线程,都自动成为前台线程)和后台线程。
这两者的区别就是:
应用程序必须运行完所有的前台线程才可以退出(如设为前台线程,即IsBackground = False, 点窗体的X关闭按钮后程序没有退出,仍在运行。);
而对于后台线程,应用程序则可以不考虑其是否已经运行完毕而直接退出,所有的后台线程在应用程序退出时都会自动结束(如设为后台线程,即IsBackground = True, 点窗体的X关闭按钮后程序立即退出。)。
将前台线程改为后台线程
static void Main(string[] args) { //创建任务类 AddParams ap = new AddParams(10,20); //创建次线程 Thread backgroundThread2 = new Thread(new ParameterizedThreadStart(Add)); backgroundThread2.IsBackground = true;//将前台线程改为后台线程 backgroundThread2.Start(ap); Console.ReadLine(); }
三、并发问题
一个应用程序域的所有线程都能够并发访问共享数据。1、使用c#的lock关键字进行同步
一旦一个线程进入锁定范围,在它退出锁定范围且释放之前,其他线程将无法访问锁定的标记。如果:试图锁定静态方法的代码,只需要声明一个私有静态对象成员作为锁定标记。
效果:
代码:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Windows.Forms; namespace ConsoleApplication1 { class Program { static void Main(string[] args) { Printer ap = new Printer(); //使用10个线程全部指向同一个对象的同一个方法。 Thread[] threads=new Thread[10]; for (int i = 0; i < 10; i++) { threads[i] = new Thread(new ThreadStart(ap.PrintNumbers));//创建次线程 threads[i].Name = string.Format("工作线程 #{0}",i); } foreach (Thread t in threads) t.Start(); Console.ReadLine(); } } public class Printer { //锁标记 private object threadLock = new object(); public void PrintNumbers() { //使用私有对象锁定标记(可以锁定一段代码) lock (threadLock) { Console.WriteLine("->{0} 正在执行 PrintNumbers",Thread.CurrentThread.Name); Console.Write("你的数字:"); for (int i = 0; i < 10; i++) { Random r = new Random(); Thread.Sleep(1000*r.Next(5)); Console.Write("{0},",i); } Console.WriteLine(); } } } }
使用当前对象作为锁定标记
public void PrintNumbers() { //使用当前对象作为锁定标记 lock (this) { //所有这个范围的代码是线程安全的 } }
2、使用System.Threading.Monitor 类型进行同步
//使用Monitor有更好的控制能力,可以使用//Monitor.Wait()指示活动线程等待一段时间
//在当前线程完成后,是用Monitor.Pulse()或Monitor.PulseAll()通知等待中的线程
public class Printer { //锁标记 private object threadLock = new object(); public void PrintNumbers() { //为指定对象上排他锁 Monitor.Enter(threadLock); try{ Console.WriteLine("->{0} 正在执行 PrintNumbers",Thread.CurrentThread.Name); Console.Write("你的数字:"); for (int i = 0; i < 10; i++) { Random r = new Random(); Thread.Sleep(1000*r.Next(5)); Console.Write("{0},",i); } }finally { Monitor.Exit(threadLock);//释放排他锁 } } }
3、使用System.Threading.Interlocked 类型进行同步
System.Threading.Interlocked:原子操作 多线程共享原子操作:执行一个单独不可分割的指令,在C#中简单的读操作或给一个小于32的字段赋值。
public void AddOne() { int intVal = 0; int newVal = Interlocked.Increment(ref intVal);//原子操作递增(同: intVal++ ) Interlocked.Exchange(ref intVal, 83);//原子操作赋值(同: intVal=83 ) Interlocked.CompareExchange(ref intVal, 99, 83);//(同:如果intVal=83,那么把99赋给intVal) }
4、使用[Synchronization]特性进行同步
using System.Runtime.Remoting.Contexts;//上下文注意要继承于:ContextBoundObject
//当CLR分配带[Synchronization]的对象时,它会把对象放到同步上下文中。 [Synchronization] public class Printer:ContextBoundObject { //................... }
四、使用TimerCallback编程
TimerCallback 定期调用的具体方法效果:
代码:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Windows.Forms; namespace ConsoleApplication1 { class Program { static void Main(string[] args) { //TimerCallback 定期调用的具体方法 TimerCallback timeCB = new TimerCallback(PrintTime); System.Threading.Timer t = new System.Threading.Timer( timeCB,//TimerCallback委托对象 null,//想传人的值(null表示没有参数) 0,//在开始之前,等待多长时间(以毫秒为单位) 1000);//每次调用的间隔时间(以毫秒为单位) Console.WriteLine("击键终止"); Console.ReadLine(); } static void PrintTime(object state) { Console.WriteLine("时间是:"+DateTime.Now.ToLongTimeString()); } } }
五、CLR 线程池
ThreadPool.QueueUserWorkItem(WaitCallback委托, 可选的自定义状态数据System.Object ); //线程池中执行方法代码:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Windows.Forms; namespace ConsoleApplication1 { class Program { //ThreadPool.QueueUserWorkItem(WaitCallback委托, 可选的自定义状态数据System.Object);//线程池中执行方法 static void Main(string[] args) { Printer p = new Printer(); WaitCallback workItem = new WaitCallback(PrintTheNumber);//线程池要执行的回调方法 for (int i = 0; i < 10; i++) { ThreadPool.QueueUserWorkItem(workItem, p);//线程池中执行方法 } Console.WriteLine("所有的任务"); Console.ReadLine(); } static void PrintTheNumber(object state) { Printer task = (Printer)state; task.PrintNumbers(); } } public class Printer { public void PrintNumbers() { lock (this) { Console.WriteLine("->{0} 正在执行 PrintNumbers", Thread.CurrentThread.Name); Console.Write("你的数字:"); for (int i = 0; i < 10; i++) { Random r = new Random(); Thread.Sleep(1000 * r.Next(5)); Console.Write("{0},", i); } } } } }
效果:
注意:
线程池中的线程是后台线程,没有固定标识线程,不便于退出,挂起或通过名字发现他。
但减少了线程的创建,开始和停止的次数,提高了效率。
相关文章推荐
- 化零为整WCF(13) - 并发控制(锁)(Mutex, Semaphore, Monitor, Lock, ThreadPool, Interlocked, ReaderWriterLock)
- 化零为整WCF(13) - 并发控制(锁)(Mutex, Semaphore, Monitor, Lock, ThreadPool, Interlocked, ReaderWriterLock)
- 稳扎稳打Silverlight(26) - 2.0线程之Lock, Interlocked, EventWaitHandle, Monitor, ThreadStaticAttribute
- 化零为整WCF(13) - 并发控制(锁)(Mutex, Semaphore, Monitor, Lock, ThreadPool, Interlocked
- 2.0线程之Lock, Interlocked, EventWaitHandle, Monitor, ThreadStaticAttribute
- 19、构建多线程应用程序:委托异步调用方法(BeginInvoke,EndInvoke,AsyncCallback)
- C#构建多线程应用程序(5) —— 使用System.Threading.Timer
- C#笔记21:多线程之线程同步中的锁定lock、Monitor
- 多线程之线程同步中的锁定lock、Monitor(转)
- 重新想象 Windows 8 Store Apps (46) - 多线程之线程同步: Lock, Monitor, Interlocked, Mutex, ReaderWriterLock
- 稳扎稳打Silverlight(26) - 2.0线程之Lock, Interlocked, EventWaitHandle, Monitor, ThreadStaticAttribute
- C#笔记21:多线程之线程同步中的锁定lock、Monitor
- 稳扎稳打Silverlight(26) - 2.0线程之Lock, Interlocked, EventWaitHandle, Monitor, ThreadStaticAttribute
- 重新想象 Windows 8 Store Apps (46) - 多线程之线程同步: Lock, Monitor, Interlocked, Mutex, ReaderWriterLock
- 多线程中的锁系统(二)-volatile、Interlocked、ReaderWriterLockSlim
- C# 多线程(lock,Monitor,Mutex,同步事件和等待句柄) (转)
- 上接稳扎稳打Silverlight(26) - 2.0线程之Lock, Interlocked, EventWaitHandle, Monitor
- C#中构建多线程应用程序
- 构建多线程应用程序
- C# 多线程(lock,Monitor,Mutex,同步事件和等待句柄)