C# 几种多线程使用总结
原文链接:https://www.jianshu.com/p/00b33e5cd149
1. Thread
//AMethod,为该线程将要执行的任务 Thread thread = new Thread(AMethod); //设置为后台线程,不设置的话默认为前台线程 thread.IsBackground = true; //启动线程 thread.Start();
2. 通过委托来开启线程
通过委托,调用BeginInvoke 方法来开启一条线程,去执行该委托所指向的方法。
// 线程执行该方法 static string test1(int x, int y) { Console.WriteLine(x + y); return x + "+"+ y; } static void Main(string[] args) { Func<int, int, string> fun1 = test1; // 通过委托来开启线程执行。 倒数第二个参数是,线程执行完毕之后的回调方法, 倒数第一个参数是,执行完之后,携带的参数。 fun1.BeginInvoke(10, 20, test1CallBack, fun1); } // 通过委托来传递回调函数,会自动将该线程的执行结果 传递过来 static void test1CallBack(IAsyncResult ar) { //AsyncState 获取所携带的参数,在这里就是上面fun1.BeginInvoke(10, 20, test1CallBack, fun1); 这行代码中,所传递的fun1 Func<int, int, string> fun1 = ar.AsyncState as Func<int, int, string>; // 获取委托所指向的方法的执行结果。 string result = fun1.EndInvoke(ar); Console.WriteLine("这就是结果" + result); }
3. 线程池
static void ThreadFunc3() { Console.WriteLine("--------"); // 调用线程池来执行任务。 每调用一次,就会开启一条线程来执行该任务。 ThreadPool.QueueUserWorkItem(test3); ThreadPool.QueueUserWorkItem(test3); ThreadPool.QueueUserWorkItem(test3); ThreadPool.QueueUserWorkItem(test3); ThreadPool.QueueUserWorkItem(test3); ThreadPool.QueueUserWorkItem(test3); ThreadPool.QueueUserWorkItem(test3); ThreadPool.QueueUserWorkItem(test3); Console.WriteLine("========"); Thread.Sleep(100); } // 线程池调用的方法,需要一个object类型的参数方法,且不能有返回值 static void test3(object obj) { Thread.CurrentThread.IsBackground = false; Console.WriteLine("asdas"); }
4. Task开启
static void ThreadMethod() { Console.WriteLine("ThreadMethod Begin" + Thread.CurrentThread.ManagedThreadId); Thread.Sleep(1000); Console.WriteLine("Thread End"); } static void continueTask(Task t) { Console.WriteLine("task is continue" + t.Id); Thread.Sleep(3000); Console.WriteLine("task is complete"); } static void Main(string[] args) { // 第一种方式开启 //Task t = new Task(ThreadMethod); //t.Start(); //// 第二种方式开启 //TaskFactory tf = new TaskFactory(); //tf.StartNew(ThreadMethod); Task t1 = new Task(ThreadMethod); Task t2 = t1.ContinueWith(continueTask); t1.Start(); // 开启任务 t1, 当t1执行完毕的时候,会执行t2的任务 Console.ReadKey(); }
5.BackgroudWorker
单独开一个线程,执行某个可能会造成前台UI无响应的耗时操作。更方便的实现进度条和获取执行结果。使用方法参见https://www.cnblogs.com/sparkdev/p/5906272.html。
private BackgroundWorker _demoBGWorker = new BackgroundWorker(); _demoBGWorker.DoWork += BGWorker_DoWork; _demoBGWorker.RunWorkerAsync(); private void BGWorker_DoWork(object sender, DoWorkEventArgs e) { //在这里执行耗时的运算。 int sum = 0; for (int i = 0; i <= 100; i++) { sum += i; } }
6. 线程使用问题
- 资源竞争
多个线程访问同一个资源,对同一个资源做修改,会引发数据混乱。 - 死锁
多把锁。互相牵制。导致线程都互相等待解锁。 - 案例:银行存取款 、多窗口同时卖票
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Threading; namespace TestProject { class SaleTicket { private Thread thread1; private Thread thread2; private Thread thread3; private int ticketCount = 1000; public SaleTicket() { thread1 = new Thread(Sale); thread2 = new Thread(Sale); thread3 = new Thread(Sale); } public void Sale() { while(true) { lock(this) { if (ticketCount > 0) { ticketCount -= 1; Console.WriteLine(Thread.CurrentThread.Name + "卖了一张票,剩余" + ticketCount); } else { Console.WriteLine(Thread.CurrentThread.Name + ":票卖完了"); break; } } } } public void StartSaleTicket() { thread1.Start(); thread2.Start(); thread3.Start(); } } }

浙公网安备 33010602011771号