CSharpe線程 目錄CSharpe線程C#如何操作線程Thread1. Thread如何開啟一個線程呢?2. Thread中常見的API3. thread的擴展封裝threadpool一、 .NET Framework2.0時代:出現了一個線程池ThreadPool二、線程池如何申請一個線程呢? ...
CSharpe線程
目錄什麼是進程?
一個應用程式的運行---對標於一個進程----虛擬詞;
所謂的進程---記錄了程式運行所消耗的各種各樣的資源;
什麼是線程?
就是電腦程式在運行的時候,執行指令的最小的執行流~ 程式
的運行---很多的併發操作,任何一個指令的執行都是需要通過線程來完成;
一個進程至少要包含一個線程;進程退出,線程也是自動消失;
什麼是多線程?
隨著技術的發展---業務的需求---需要指令的併發執行;
同時執行多種指令(線程來執行的);
和CPU的核數有關~~
C#如何操作線程
- Thread(很少用)
- ThreadPool(線程池)
- Task(主流-----重點)
Thread
Thread:來自於System.Threading的一個密封類,它是在.net Framwork1.0時代出現的,在C#中用來操作電腦資源線程的一個幫助類庫;
1. Thread如何開啟一個線程呢?
多線程因為是無序的,調試不太好調試,只能通過寫日誌,輸出結果,根據結果來判斷thread的特點.
private void btn_Thread_Click(object sender, EventArgs e)
{
Debug.WriteLine($"***************Main Thread start: {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("HH:mm:ss.fff")} **************** ");
Thread thread = new Thread(() =>
{
Debug.WriteLine($"***************Thread start: {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("HH:mm:ss.fff")} **************** ");
Debug.WriteLine($"***************Thread end: {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("HH:mm:ss.fff")} **************** ");
});
thread.Start();
Debug.WriteLine($"***************Main Thread end: {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("HH:mm:ss.fff")} **************** ");
}
結果
2. Thread中常見的API
thread.Suspend(); // 線程暫停
thread.Resume(); // 線程恢復
thread.Abort(); // 線程終止
1.線程等待的:ThreadState有多種狀態;如果線程停止了,狀態會修改;
while (thread.ThreadState != System.Threading.ThreadState.Stopped) //如果線程沒有停止;
{
Thread.Sleep(500); //當前休息500ms 不消耗電腦資源的
}
2.自己支持的線程等待:
thread.Join();//等待線程中的內容執行完畢;繼續往後;
thread.Join(500);//等待500ms,過時不候;
thread.Join(TimeSpan.FromMilliseconds(500));//等待500ms,過時不候;
thread.IsBackground = true;// 是後臺線程:程式強制關掉,線程也就隨之消失了;
thread.IsBackground = false; //是前臺線程:程式強制關掉,線程會等待,內部的行為執行完畢,然後才結束;
thread.Start();
3. thread的擴展封裝
多線程;非同步執行;
不阻塞界面;
無序性---多個動作。如果使用多線程,是無法控制順序的。
現在有兩個動作 使用了2個委托 必須是多線程執行的 要求兩個委托按順序執行。
Action action = () => { Debug.WriteLine("this is first run"); };
Action action2 = () => { Debug.WriteLine("this is second run"); };
private void button1_Click(object sender, EventArgs e)
{
callBack(action, action2);
}
private void callBack(Action action,Action action1)
{
Thread t= new Thread(() =>
{
action();
action1();
});
t.Start();
}
如果有一個帶返回值的委托,需要你要多線程執行;
Func<int> func = () => { return DateTime.Now.Year; };
private void button1_Click(object sender, EventArgs e)
{
Func<int> func1= CallBack<int>(func);
Debug.WriteLine("t****************");
Debug.WriteLine("t****************");
Debug.WriteLine("t****************");
Debug.WriteLine("t****************");
Debug.WriteLine("t****************");
Debug.WriteLine("t****************");
int iResult=func1();
Debug.WriteLine(iResult);
}
private Func<T> CallBack<T>(Func<T> func)
{
T result = default(T);
Thread t = new Thread(() =>
{
result = func();
});
t.Start();
return new Func<T>(() => {
t.Join();
return result; });
}
threadpool
Thread對比Threadpool:Api很多,功能繁多;使用起來,不好控制;讓開發者試用起來並不友好;
Thread對線程的數量管控,全部都需要讓程式員自己來管控;
一、 .NET Framework2.0時代:出現了一個線程池ThreadPool
是一種池化思想,相當於是在池子中,有線程存在;如果需要使用線程;就可以直接到線程池中去獲取直接使用,如果使用完畢,在自動的回放到線程池中去;
好處:
1.不需要程式員對線程的數量管控,提高性能,放置濫用
2.去掉了很多在Thread中沒有必要的Api
二、線程池如何申請一個線程呢?
ThreadPool.QueueUserWorkItem((state) =>
{
Debug.WriteLine($"***************ThreadPool start: {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("HH:mm:ss.fff")} **************** ");
Thread.Sleep(5000);
Debug.WriteLine($"***************ThreadPool end: {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("HH:mm:ss.fff")} **************** ");
});
三、線程等待
- 觀望式的:
- 定義一個監聽ManualResetEvent
- 通過ManualResetEvent.WaitOne等待
- 等到ManualResetEvent.Set方法執行,方法執行完畢後,主線程就繼續往後;
ManualResetEvent manualResetEvent = new ManualResetEvent(false);
ThreadPool.QueueUserWorkItem((state) =>
{
Debug.WriteLine($"***************ThreadPool start: {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("HH:mm:ss.fff")} **************** ");
Thread.Sleep(5000);
Debug.WriteLine($"***************ThreadPool end: {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("HH:mm:ss.fff")} **************** ");
manualResetEvent.Set();
});
manualResetEvent.WaitOne();
Debug.WriteLine($"***************Main Thread end: {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("HH:mm:ss.fff")} **************** ");
}
四、線程池如何控制線程數量
如果通過SetMinThreads/SetMaxThreads來設置線程的數量;這個數量訪問是在當前進程是全局的;
{
int workerThreads = 4;
int completionPortThreads = 4;
ThreadPool.SetMinThreads(workerThreads, completionPortThreads);
}
{
int workerThreads = 8;
int completionPortThreads = 8;
ThreadPool.SetMaxThreads(workerThreads, completionPortThreads);
}
{
ThreadPool.GetMinThreads(out int workerThreads, out int completionPortThreads);
Debug.WriteLine($"當前進程最小的工作線程數量:{workerThreads}");
Debug.WriteLine($"當前進程最小的IO線程數量:{completionPortThreads}");
}
{
ThreadPool.GetMaxThreads(out int workerThreads, out int completionPortThreads);
Debug.WriteLine($"當前進程最大的工作線程數量:{workerThreads}");
Debug.WriteLine($"當前進程最大的IO線程數量:{completionPortThreads}");
}
Task
一、Task開啟線程有哪些方式
Action action = () =>
{
Console.WriteLine($"***************Task start: {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("HH:mm:ss.fff")} **************** ");
Console.WriteLine("啟動了一個新的線程");
Console.WriteLine($"***************Task end: {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("HH:mm:ss.fff")} **************** ");
};
Task task = new Task(action);
task.Start();
Task.Run (() =>
{
Console.WriteLine($"***************Task.Run start: {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("HH:mm:ss.fff")} **************** ");
Console.WriteLine("啟動了一個新的線程");
Console.WriteLine($"***************Task.Run end: {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("HH:mm:ss.fff")} **************** ");
});
TaskFactory taskFactory = new TaskFactory();
taskFactory.StartNew(() =>
{
Console.WriteLine($"***************TaskFactory start: {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("HH:mm:ss.fff")} **************** ");
Console.WriteLine("啟動了一個新的線程");
Console.WriteLine($"***************TaskFactory end: {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("HH:mm:ss.fff")} **************** ");
});
Task.Factory.StartNew(() =>
{
Console.WriteLine($"***************Task.Factory start: {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("HH:mm:ss.fff")} **************** ");
Console.WriteLine("啟動了一個新的線程");
Console.WriteLine($"***************Task.Factory end: {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("HH:mm:ss.fff")} **************** ");
});
啟動的多線程的特點:
- 不阻塞主線程----不會卡頓界面
- 線程的啟動---由操作系統來調度啟動; 延遲啟動(延遲很短)
- 併發執行~~
線程執行完畢就銷毀了嗎?
ThreadPool 線程池----Task 線程都是來自於線程池的;
多進程技術的使用場景的分析
問題:儘可能的多啟動線程?? 萬萬不可的,一定要適當的使用;
一堆業務邏輯: 項目要開發 10個板塊
單線程執行: 一個人去承擔這個項目開發----一步一步的做;一個版快一個板塊的去開發; 開發周期時間長
多線程執行: 一個團隊開發: 效率更高~~ 多個人可以分工開發;
類比: 一個人(開支小)和一個團隊(10個人 10份工資);
線程等待
有Delay 和 Sleep兩種方式來進行線程的等待.
{
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
Task.Delay(3000);
stopwatch.Stop();
Console.WriteLine($"time:{stopwatch.ElapsedMilliseconds}");
}
{
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
Thread.Sleep(3000);
stopwatch.Stop();
Console.WriteLine($"time:{stopwatch.ElapsedMilliseconds}");
}
結果為:
Task.Delay().ContinueWith() 不阻塞主線程,等待多長時間之後,可以執行一段業務邏輯----回調函數
Thread.Sleep() 阻塞主線程,主線程等待指定時間後再運行。
線程等待的多種方案
Task<int> task = Task.Run(() =>
{
Thread.Sleep(3000);
Console.WriteLine("Open new thread!");
return 10;
});
int num = task.Result; //等待task執行完畢,獲取返回值,會阻塞當前線程
//下麵是沒有返回值方法調用的時候,使用的方法
//Task.WaitAll(task); //等待task執行完畢,會阻塞當前線程
//int i = Task.WaitAny(task); //等待task執行完畢,會阻塞當前線程
什麼場景下可以使用多線程呢?(可以併發的時候) 不適合使用多線程??
故事: 高級班的項目實戰---逐個講解知識點,然後項目實戰,分工合作,分小組開發;
- 逐個講解知識點 -----可以多線程來模擬?---只有Richard老師一個人講解----不可用;不能併發,不能多線程來模擬
- 項目實戰,分工合作,分小組開發; -----可以多線程來模擬?---有多個人同時開發,可以分工併發開發,可以多線程開發~~
模擬的代碼
/// <summary>
/// 模擬講課的方法
/// </summary>
/// <param name="lesson">課程名</param>
private void Tech(string lesson)
{
Console.WriteLine($"{lesson} ||開始了.....");
long iResult = 0;
for (int i = 0;i<1_000_000_000;i++)
{
iResult += i;
}
Console.WriteLine($"{lesson} ||講完了.....");
}
/// <summary>
/// 模擬不同人開發的方法
/// </summary>
/// <param name="name"></param>
/// <param name="projectName"></param>
private void Coding(string name,string projectName)
{
Console.WriteLine($"************************* Coding Start || {name} {projectName} {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} ****************");
long iResult = 0;
for (int i = 0; i < 1_000_000_000; i++)
{
iResult += i;
}
Console.WriteLine($"************************* Coding End || {name} {projectName} {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} ****************");
}
基礎的項目流程
private void button5_Click(object sender, EventArgs e)
{
Console.WriteLine("同學們!開始上課了");
Tech("泛型");
Tech("委托");
Tech("多線程");
Tech("非同步編程");
Tech("併發編程");
Console.WriteLine("知識點講解完畢了~~開始項目實戰開發`~~~");
TaskFactory factory = new TaskFactory();
factory.StartNew(() => Coding("張三", "資料庫設計"));
factory.StartNew(() => Coding("李四", "框架的搭建"));
factory.StartNew(() => Coding("王五", "Wechat Pay"));
factory.StartNew(() => Coding("趙六", "Web Api"));
factory.StartNew(() => Coding("田七", "封裝通用的組件"));
factory.StartNew(() => Coding("劉八", "編譯"));
factory.StartNew(() => Coding("楊九", "發行"));
}
需求一、所有人的任務都執行完成後,小聚一下,大吃一頓```
private void button5_Click(object sender, EventArgs e)
{
List<Task> tasks = new List<Task>();
Console.WriteLine("同學們!開始上課了");
Tech("泛型");
Tech("委托");
Tech("多線程");
Tech("非同步編程");
Tech("併發編程");
Console.WriteLine("知識點講解完畢了~~開始項目實戰開發`~~~");
TaskFactory factory = new TaskFactory();
tasks.Add( factory.StartNew(() => Coding("張三", "資料庫設計")));
tasks.Add(factory.StartNew(() => Coding("李四", "框架的搭建")));
tasks.Add(factory.StartNew(() => Coding("王五", "Wechat Pay")));
tasks.Add(factory.StartNew(() => Coding("趙六", "Web Api")));
tasks.Add(factory.StartNew(() => Coding("田七", "封裝通用的組件")));
tasks.Add(factory.StartNew(() => Coding("劉八", "編譯")));
tasks.Add(factory.StartNew(() => Coding("楊九", "發行")));
Task.WaitAll(tasks.ToArray());
Console.WriteLine("項目開發完畢了~~~,去大吃一頓~~");
}
需求2、 開發人員中,只要其中有一個執行完成了,Richard老師就準備發佈環境,準備發佈部署
private void button5_Click(object sender, EventArgs e)
{
List<Task> tasks = new List<Task>();
Console.WriteLine("同學們!開始上課了");
Tech("泛型");
Tech("委托");
Tech("多線程");
Tech("非同步編程");
Tech("併發編程");
Console.WriteLine("知識點講解完畢了~~開始項目實戰開發`~~~");
TaskFactory factory = new TaskFactory();
tasks.Add( factory.StartNew(() => Coding("張三", "資料庫設計")));
tasks.Add(factory.StartNew(() => Coding("李四", "框架的搭建")));
tasks.Add(factory.StartNew(() => Coding("王五", "Wechat Pay")));
tasks.Add(factory.StartNew(() => Coding("趙六", "Web Api")));
tasks.Add(factory.StartNew(() => Coding("田七", "封裝通用的組件")));
tasks.Add(factory.StartNew(() => Coding("劉八", "編譯")));
tasks.Add(factory.StartNew(() => Coding("楊九", "發行")));
{
Task.WaitAny(tasks.ToArray()); //等待一堆任務中,其中有一個執行完成了,繼續往後執行~
Console.WriteLine("XXX 完成了開發任務~~,Richard老師就準備發佈環境,準備發佈部署");
}
{
Task.WaitAll(tasks.ToArray());
Console.WriteLine("項目開發完畢了~~~,去大吃一頓~~");
}
}
使用場景:
Task.WaitAll----系統首頁---包含了很多的信息---都是後臺提供----獲取這個結果的時候;準備一個複雜實體---包含各種信息 查詢這些數據---可以多線程去執行;同時查詢;
查詢必須要獲取到所有的數據----要獲取所有的數據----Task.WaitAll
Task.WaitAny----查詢一條數據----數據來源可能是不同的地方,資料庫/緩存/介面/讀取硬碟中的數據
1.傳統做法: 先查詢緩存試試看,如果沒有,再查詢資料庫,如果沒有,再繼續往後,直到查詢到數據為止;
2.有四個渠道獲取數據----> 只要有一個渠道獲取到數據就Ok, 直接啟動四個線程去查詢; 等待其中有一個線程執行完成,特殊處理,如果查詢到數據後,就結束~~ 只要有一個執行結束了,就已經拿到數據了,其他的不用管了~~
需求3、 有沒有可以不阻塞主線程,也能達到效果;
private void button2_Click(object sender, EventArgs e)
{
List<Task> tasks = new List<Task>();
Console.WriteLine("同學們!開始上課了");
Tech("泛型");
Tech("委托");
Tech("多線程");
Tech("非同步編程");
Tech("併發編程");
Console.WriteLine("知識點講解完畢了~~開始項目實戰開發`~~~");
TaskFactory factory = new TaskFactory();
tasks.Add(factory.StartNew(Object => Coding("張三", "資料庫設計"),"張三"));
tasks.Add(factory.StartNew(Object => Coding("李四", "框架的搭建"), "李四"));
tasks.Add(factory.StartNew(Object => Coding("王五", "Wechat Pay"), "王五"));
tasks.Add(factory.StartNew(Object => Coding("趙六", "Web Api"), "趙六"));
tasks.Add(factory.StartNew(Object => Coding("田七", "封裝通用的組件"), "田七"));
tasks.Add(factory.StartNew(Object => Coding("劉八", "編譯"), "劉八"));
tasks.Add(factory.StartNew(Object => Coding("楊九", "發行"), "楊九"));
{
factory.ContinueWhenAny(tasks.ToArray(), (task) =>
{
Console.WriteLine($"{task.AsyncState} 完成了開發任務~~,發一個小紅包");
});
}
{
factory.ContinueWhenAll(tasks.ToArray(), (task) =>
{
Console.WriteLine("項目開發完畢了~~~,去大吃一頓~~");
});
}
}
需求4、如果想要完成以上需求,要求不阻塞主線程,如果也沒有ContinueWhenAll api.
Task.Run(() =>
{
Task.WaitAll(tasks.ToArray());
Console.WriteLine("項目開發完畢了~~~,去大吃一頓~~");
});
通過Task返回一個字元串
{
List<Task<string>> tasklist = new List<Task<string>>();
for (int i = 0; i < 3; i++)
{
string k = $"{i}";
tasklist.Add(Task.Run(() =>
{
return $"{k}_Task";
}));
}
Task.Run(() =>
{
Task.WaitAny(tasklist.ToArray());
Task<string> task = tasklist.First(c => c.Status == TaskStatus.RanToCompletion);
Console.WriteLine(task.Result);
});
}
Paralell
如何批量開啟10個線程?
Parallel.For(0, 10, (i) =>
{
Console.WriteLine($"Thread id : {Thread.CurrentThread.ManagedThreadId.ToString("00") }") ;
});
如何控制啟動線程的數量?
ParallelOptions parallelOptions = new ParallelOptions();
parallelOptions.MaxDegreeOfParallelism = 10;
Parallel.For(0, 10100, parallelOptions, (i) =>
{
Console.WriteLine($"Thread id : {Thread.CurrentThread.ManagedThreadId.ToString("00") }");
});
線程異常處理
1.try_catch捕獲不到多線程內部的異常.
按照正常的Try Catch來處理異常。
try {
for (int i = 0; i < 20; i++)
{
string str = $"Advance_{i}";
Task.Run(() =>
{
if (str.Equals("Advance_7"))
{
throw new Exception("Advance_7異常");
}
else if (str.Equals("Advance_10"))
{
throw new Exception("Advance_{10}異常");
}
else if (str.Equals("Advance_15"))
{
throw new Exception("Advance_15異常");
}
else if (str.Equals("Advance_18"))
{
throw new Exception("Advance_18異常");
}
else
{
Console.WriteLine(str);
}
});
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
2.如何捕捉線程內部的異常,try-catch 包裹,線程等待; 可以捕捉到AggregateException類型的異常;
3.一個try可以對應多個catch 發生異常後,catch捕捉,是從上往下匹配異常類型,只要是匹配到異常類型後,就進入開始處理異常;
4.如何輸出消息, 要轉換成AggregateException,獲取InnerExceptions 的集合,多線程發生的多個異常,都在這個集合中;
private void button4_Click(object sender, EventArgs e)
{
List<Task> tasks = new List<Task>();
try {
for (int i = 0; i < 20; i++)
{
string str = $"Advance_{i}";
Task task = Task.Run(() =>
{
if (str.Equals("Advance_7"))
{
throw new Exception("Advance_7異常");
}
else if (str.Equals("Advance_10"))
{
throw new Exception("Advance_{10}異常");
}
else if (str.Equals("Advance_15"))
{
throw new Exception("Advance_15異常");
}
else if (str.Equals("Advance_18"))
{
throw new Exception("Advance_18異常");
}
else
{
Console.WriteLine(str);
}
});
tasks.Add(task);
}
Task.WaitAll(tasks.ToArray());
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
線程取消
有一個需求:
首頁---數據塊---考情/周top10/月top ......
啟動四個線程去獲取數據,要正常展示----一定要四個線程都能正常獲取到數據,必然要等待四個線程都執行結束;
場景:四個線程,有某一個線程異常了~~ 整塊數據不能用; 如果有異常,其他的正常的線程,其實查詢也沒有價值,既然沒有異常的線程執行也沒價值,就應該取消-----(因為線程在執行業務邏輯---需要消耗電腦的資源,電腦的資源是有限的)
標準方案:
定義一個cts,包含一個IsCancellationRequested 屬性,預設值為=false,同時提供了一個Cancel方法, IsCancellationRequested: 預設的false ----true; IsCancellationRequested 屬性 只能通過Cancel來變化,不能通過其他的渠道修改;
CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
try {
for (int i = 0; i < 50; i++)
{
string str = $"Advance_{i}";
Task.Run(() =>
{
if (cancellationTokenSource.IsCancellationRequested == false)
{
Console.WriteLine("正常運行");
if (str.Equals("Advance_7"))
{
cancellationTokenSource.Cancel();
throw new Exception("Advance_7異常");
}
else if (str.Equals("Advance_10"))
{
cancellationTokenSource.Cancel();
throw new Exception("Advance_{10}異常");
}
else if (str.Equals("Advance_15"))
{
cancellationTokenSource.Cancel();
throw new Exception("Advance_15異常");
}
else if (str.Equals("Advance_18"))
{
cancellationTokenSource.Cancel();
throw new Exception("Advance_18異常");
}
}
else
{
Console.WriteLine("線程非正常退出");
}
});
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
多線程的中間變數
先看一段代碼
for (int i = 0; i < 10000; i++)
{
Task.Run(() => Console.WriteLine($"{i}"));
}
輸出的都是10000
為什麼會這樣那:
int i = 0; 開始迴圈,定義好的一個變數;
線程是延遲啟動,啟動線程不阻塞UI線程; 多線程要執行邏輯,要使用i,i已經是20了;
要實現我們的目的
for (int i = 0; i < 10000; i++)
{
int k = i;
Task.Run(() => Console.WriteLine($"{k}"));
}
線程安全
線程不安全:多線程在執行業務邏輯的時候,得到的結果,如果和單線程執行的結果如果不一致,那就是線程不安全~~
線程安全:單線程執行的結果要和多線程執行的結果要一致;線程安全的;
有多線程不安全的代碼
private void button6_Click(object sender, EventArgs e)
{
List<int> list = new List<int>();
List<Task> tasks = new List<Task>();
for (int i = 0; i < 10000; i++)
{
tasks.Add(Task.Run(() => { list.Add(i); }));
}
Task.WaitAll(tasks.ToArray());
Console.WriteLine(list.Count);
}
如何解決線程安全呢?
- 鎖, ----控制執行的線程只能有一個
- 直接使用單線程;
- 使用線程安全對象 看看數據結構 線程安全對象 List/Arraylist 都不是線程安全的集合--把list Arraylist 換成安全對象;
- 通過演算法+拆分做到---劃塊操作數據; 原理:還是單線程去操作一塊數據;
private readonly static object obj = new object();
private void button6_Click(object sender, EventArgs e)
{
List<int> list = new List<int>();
List<Task> tasks = new List<Task>();
for (int i = 0; i < 10000; i++)
{
tasks.Add(Task.Run(() =>
{
//鎖: 控制鎖內部的代碼執行,只能有一個線程進入執行,必須要等進入鎖的線程執行結束了,其他的線程才能再進去一個; 反多線程;
lock (obj)
{
list.Add(i);
}
}));
}
Task.WaitAll(tasks.ToArray());
Console.WriteLine(list.Count);
}