任何的異步多線程,都是跟委托相關,沒有委托,啥也沒有
多線程1.0 1.1 Thread
Thread:是.Net架構封裝的一個類,描述線程這個東西
同步方法:發起一個調用,一定要等着計算結束才運作下一行
異步方法:發起一個調用,不會等着計算結束,而是直接開始運作下一行
異步多線程:多線程說的 是CLR線程 異步IO線程
- 同步方法卡界面,以為ui線程忙于計算,異步多線程方法不卡界面,主線程閑置,計算任務交給子線程在做;
- 同步方法慢,隻有一個線程計算,異步多線程方法快,多個線程并發計算;多線程的資源消耗更多,線程并不是越多越好(資源有限/管理線程也消耗資源)
- 異步多線程是無序的,啟動無序 執行時間不确定 結束無序,所有我們不要試圖通過順序或者時間等待來控制流程
怎麼控制順序?
回調 等待 狀态
AsyncCallback 回調
IsCompleted 狀态
iAsyncResult.AsyncWaitHandle.WaitOne()//一直等待任務完成,第一時間進入下一行
iAsyncResult.AsyncWaitHandle.WaitOne(-1)//一直等待任務完成,第一時間進入下一行
iAsyncResult.AsyncWaitHandle.WaitOne(1000)//最多等待1000毫秒,否在進入下一行
Act.EndInvoke(iAsyncResult);//等待
String resultIn=func.EndInvoke(ar);//對于每個異步調用,隻能調用一次 EndInvoke
ThreadStart threadStart = new ThreadStart(() =>
{
Thread.Sleep(5000);
this.DoSomethingLong("btnThreads_Click");//這是一個自己定義的私有方法
});
Thread thread = new Thread(threadStart);
thread.IsBackground = true;//變成背景線程
thread.Start();//預設是前台線程,UI線程退出後,還會繼續執行完;背景線程就直接退出了
//thread.Suspend();//暫停
//thread.Resume();//恢複
//thread.Join();//做等待
//thread.Abort();//銷毀
//while (thread.ThreadState != System.Threading.ThreadState.Running)
//{//線程是否還在運作,以可以起到等待的效果
//}
ThreadPool 線程池 2.0 封裝
- 去掉各種 api 避免濫用,降低複雜度
- 池化:1減少建立/銷毀的成本 2 限制最大線程數量
ThreadPool.QueueUserWorkItem(o=>
{
Thread.Sleep(5000);
//代碼段
});
沒有需求,就别等待,阻塞線程
ManualResetEvent 狀态标
ManualResetEvent mre = new ManualResetEvent(false);
mre.WaitOne();//狀态标為false時,不會過這個方法
mre.Set();//打開
mre.Reset();//關閉
ManualResetEvent mre = new ManualResetEvent(false);//false 關閉
new Action(() =>
{
Thread.Sleep(5000);
Console.WriteLine("委托的異步調用");
mre.Set();//打開
}).BeginInvoke(null, null);
mre.WaitOne();
Console.WriteLine("12345");
mre.Reset();//關閉
new Action(() =>
{
Thread.Sleep(5000);
Console.WriteLine("委托的異步調用2");
mre.Set();//打開
}).BeginInvoke(null, null);
mre.WaitOne();
Console.WriteLine("23456");
Task 3.0
使用的是線程池的線程 全部是背景線程
API很強大
多線程:業務是可以并發執行
千萬不要在Task裡面去啟動Task
啟動:
Task.Run();
TaskFactory taskFactory=Task.Factory;//new TaskFactory();
taskFactory.StartNew();//啟動
Task.WaitAny(taskList.toArray());//執行玩一個線程後開始執行
Task.WaitAll();//全部執行玩
//回調
taskFactory.ContinueWhenAll();
taskFactory.ContinueWhenAny();
4.0 Parallel
跟task很像 ==task+waitall 啟動多個線程計算 主線程也參與計算,節約了一個線程
Parallel.Invoke();//
Parallel.For(0,5,t=>
{
//代碼塊
})
Parallel.ForEach(new int[]{1,2,3,4,5}, options, (t,state)=>
{
//代碼塊
this.DoSomethingLog($”btnParallel_click_00{t}”);
//state.Stop();//
//state.Break();//
});
ParalleOptions options=new ParalleOptions()//同時線上 線程
{
MaxDegreeOfParalleLism=3
};
ThreadCore 異常處理
多線程裡的異常是會被丢掉,除非waitall
建議 多線程裡面,是不允許異常,以就是内部try catch,自己處理
線程取消
線程取消不是操作線程,而是操作共享信号量(共享變量,多個線程都能通路到的東西,變量/資料庫的資料/硬碟資料)
每個線程在執行的過程中,程序去超快下這個信号量,然後自己結束自己
線程不能别人終止,隻能自己幹掉自己,延遲是少不了的
CancellationTakensource cts = new CancellationTakensource(); //bool值
cts.IsCancllationRequested //檢查信号
cts.Cancel();//
多線程臨時變量
For(int i=0;i<5;i++){
Int k-i;
New Action(()=>
{
Thread.Sleep(100);
Console.Writeline(k);
Console.Writeline(i);
}).BeginInvoke(null,null);
}
i 隻有一個,真實 實際的時候,已經是5了
k 有多個,每次都是獨立的k,跟i沒有關系
線程安全
公有變量:都能通路局部變量/全局變量/資料庫的一個值/硬碟檔案
線程内部不共享的是安全的
多個線程同時操作一個變量,變量的值可能被覆寫
解決多線程沖突第一個辦法:lock的方法塊裡面是單線程 去掉多線程,lock裡面的代碼要盡量的少
Lock(){//lock後的方法塊,任意時刻隻有一個線程可以進入
}
解決多線程沖突第二個辦法:沒有沖突,從資料上隔離開
Lock==Monitor.Enter 檢查下這個變量,有沒有被lock 有就等着,沒有就占有者,然後進去執行,執行玩釋放
lock(this)鎖定目前執行個體,别的地方如果要使用這個變量呢?都被鎖定了
如果每個執行個體想要單獨的鎖定,private object
String a=”123456’ lock(a) string b=”123456” 享元模式的記憶體配置設定,字元串值是唯一的,會鎖定的變量b
Private static readonly object btnThreadCore_Click_Lock=new object();
await/async C#5.0 .Net Framework4.5
await/async 是文法糖,本身是編譯器提供的功能
async 用來修飾方法的
await 隻能出現在Task 前面
await/async 本身不是線程
不推薦void傳回值,使用Task來代替
Task和Task能夠使用await,Task.WhenAny,Task.Whenall等方式組合使用,Async Void不行
無傳回值
主線程越到await傳回,執行主線程任務
子線程執行 其實是封裝成委托,在task之後成為回調(編譯器功能 狀态機實作)
這個回調的線程是不确定的:可能是主線程,可能是子線程 以可能是其他的線程
t.Wait();//主線程等待Task完成 或者t.Result;