天天看點

進一步了解多線程

任何的異步多線程,都是跟委托相關,沒有委托,啥也沒有

多線程1.0 1.1 Thread

Thread:是.Net架構封裝的一個類,描述線程這個東西

同步方法:發起一個調用,一定要等着計算結束才運作下一行

異步方法:發起一個調用,不會等着計算結束,而是直接開始運作下一行

異步多線程:多線程說的 是CLR線程 異步IO線程

  1. 同步方法卡界面,以為ui線程忙于計算,異步多線程方法不卡界面,主線程閑置,計算任務交給子線程在做;
  2. 同步方法慢,隻有一個線程計算,異步多線程方法快,多個線程并發計算;多線程的資源消耗更多,線程并不是越多越好(資源有限/管理線程也消耗資源)
  3. 異步多線程是無序的,啟動無序 執行時間不确定 結束無序,所有我們不要試圖通過順序或者時間等待來控制流程

怎麼控制順序?

回調 等待 狀态

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 封裝

  1. 去掉各種 api 避免濫用,降低複雜度
  2. 池化: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;