免费高清特黄a大片,九一h片在线免费看,a免费国产一级特黄aa大,国产精品国产主播在线观看,成人精品一区久久久久,一级特黄aa大片,俄罗斯无遮挡一级毛片

分享

并行編程和任務(wù)(二)

 python_lover 2020-05-11

前言

  上一篇我們主要介紹了并行編程相關(guān)的知識(shí),這一節(jié)我們繼續(xù)介紹關(guān)于任務(wù)相關(guān)的知識(shí)。為了更好的控制并行操作,我們可以使用System.Threading.Tasks中的Task類。我們首先來了解是什么是任務(wù)——任務(wù)表示將要完成的一個(gè)或某個(gè)工作單元,這個(gè)工作單元可以在單獨(dú)線程中運(yùn)行,也可以使用同步方式啟動(dòng)運(yùn)行(需要等待主線程調(diào)用)。為什么使用任務(wù)呢?——任務(wù)不僅可以獲得一個(gè)抽象層(將要完成的工作單元)、還可以對(duì)底層的線程運(yùn)行進(jìn)行更好更多的控制(任務(wù)的運(yùn)行)。

使用線程池的任務(wù)

  我們講到使用任務(wù)可以更好更多的控制底層的線程。就涉及到——線程池,線程池提供的是一個(gè)后臺(tái)線程的池。線程池獨(dú)自管理線程、根據(jù)需求增加或減少線程數(shù)。使用完成的線程返回至線程池中。我們下面就看看創(chuàng)建任務(wù):

我們看下創(chuàng)建任務(wù)的幾種方式:

1、使用實(shí)例化的TaskFactory類,然后使用其StartNew方法啟動(dòng)任務(wù)。

2、使用Task靜態(tài)的Factory以來訪問TaskFactory,然后調(diào)用StartNew方法啟動(dòng)任務(wù)。與第一種相似,但是對(duì)工廠的創(chuàng)建的控制就沒那么全面。

3、使用Task的構(gòu)造函數(shù),實(shí)例化Task對(duì)象來指定創(chuàng)建任務(wù),然后通過Start()方法進(jìn)行啟動(dòng)任務(wù)。

4、使用Task.Run方法來立即啟動(dòng)任務(wù)。

  我們看下以上方法創(chuàng)建的任務(wù)有何區(qū)別和相同吧,看代碼:

        private static object _lock = new object();
     public
static void TaskMethond(object item) {lock (_lock) { Console.WriteLine(item?.ToString()); Console.WriteLine($"任務(wù)Id:{Task.CurrentId?.ToString() ?? "沒有任務(wù)運(yùn)行"}\t 線程Id:{Thread.CurrentThread.ManagedThreadId}"); Console.WriteLine($"是否是線程池中的線程:{Thread.CurrentThread.IsThreadPoolThread}"); Console.WriteLine($"是否是后臺(tái)線程:{Thread.CurrentThread.IsBackground}"); Console.WriteLine(); } }#region 任務(wù)創(chuàng)建public static void TaskCreateRun() {var taskFactory = new TaskFactory();var task1 = taskFactory.StartNew(TaskMethond, "使用實(shí)例化TaskFactory");var task2 = Task.Factory.StartNew(TaskMethond, "使用Task靜態(tài)調(diào)用Factory");var task3 = new Task(TaskMethond, "使用Task構(gòu)造函數(shù)實(shí)例化"); task3.Start();var task4 = Task.Run(() => TaskMethond("使用Task.Run")); }#endregion

  我們看代碼運(yùn)行的結(jié)果,發(fā)現(xiàn)不管使用的那種方法創(chuàng)建任務(wù),都是使用過的線程池中的線程。

使用單獨(dú)線程的任務(wù)

  任務(wù)當(dāng)然也不一定就是使用線程池中的線程運(yùn)行的,也是可以使用其他線程的。如果任務(wù)的將長(zhǎng)時(shí)間運(yùn)行的話,我們盡可能的考慮使用單獨(dú)線程運(yùn)行(TaskCreationOptions.LongRunning),這個(gè)情況下線程就不由線程池管理。我們看下不使用線程池中線程運(yùn)行,使用單獨(dú)線程運(yùn)行的任務(wù)。

        #region 單獨(dú)線程運(yùn)行任務(wù)public static void OnlyThreadRun()
        {var task1 = new Task(TaskMethond, "單獨(dú)線程運(yùn)行任務(wù)", TaskCreationOptions.LongRunning);
            task1.Start();
        }#endregion

  我們看其運(yùn)行結(jié)果,依舊是后臺(tái)線程,但是不是使用的線程池中的線程。

使用同步任務(wù)

同時(shí)任務(wù)也可以同步運(yùn)行,以相同的線程作為調(diào)用線程,下面我們看下使用Task類中的RunSynchronoushly方法實(shí)現(xiàn)同步運(yùn)行。

        #region 同步任務(wù)public static void TaskRunSynchronoushly()
        {
            TaskMethond("主線程調(diào)用");var task1 = new Task(TaskMethond, "任務(wù)同步調(diào)用");
            task1.RunSynchronously();
        }#endregion

我們看運(yùn)行結(jié)果,發(fā)現(xiàn)首先調(diào)用TaskMethond方法時(shí)候沒有任務(wù)并且使用的線程1,再我們創(chuàng)建Task實(shí)例運(yùn)行TaskMethond方法的時(shí)候,任務(wù)id是1,但是線程我們依然使用的是主線程1。 

連續(xù)任務(wù)

在任務(wù)中,我們可以指定在某個(gè)任務(wù)完成后,應(yīng)該馬上開始另外一個(gè)任務(wù)。好比一個(gè)任務(wù)完成之后應(yīng)該繼續(xù)其處理。但是失敗后我們應(yīng)該進(jìn)行一些處理工作。

我們可以使用ContinueWith()方法來定義使用連續(xù)任務(wù),表示某任務(wù)之后應(yīng)該開始其他任務(wù),我們也可以指定任務(wù)成功后開始某個(gè)任務(wù)或者失敗后開啟某個(gè)任務(wù)(TaskContinuationOptions)。

        #region 連續(xù)任務(wù)public static void TaskOne()
        {
            Console.WriteLine($"任務(wù){(diào)Task.CurrentId},方法名:{System.Reflection.MethodBase.GetCurrentMethod().Name }啟動(dòng)");
            Task.Delay(1000).Wait();
            Console.WriteLine($"任務(wù){(diào)Task.CurrentId},方法名:{System.Reflection.MethodBase.GetCurrentMethod().Name }結(jié)束");
        }public static void TaskTwo(Task task)
        {
            Console.WriteLine($"任務(wù){(diào)task.Id}以及結(jié)束了");
            Console.WriteLine($"現(xiàn)在開始的 任務(wù)是任務(wù){(diào)Task.CurrentId},方法名稱:{System.Reflection.MethodBase.GetCurrentMethod().Name  }  ");
            Console.WriteLine($"任務(wù)處理");
            Task.Delay(1000).Wait();
        }public static void TaskThree(Task task)
        {
            Console.WriteLine($"任務(wù){(diào)task.Id}以及結(jié)束了");
            Console.WriteLine($"現(xiàn)在開始的 任務(wù)是任務(wù){(diào)Task.CurrentId}.方法名稱:{System.Reflection.MethodBase.GetCurrentMethod().Name } ");
            Console.WriteLine($"任務(wù)處理");
            Task.Delay(1000).Wait();
        }public static void ContinueTask()
        {
            Task task1 = new Task(TaskOne);
            Task task2 = task1.ContinueWith(TaskTwo, TaskContinuationOptions.OnlyOnRanToCompletion);//已完成情況下繼續(xù)任務(wù)Task task3 = task1.ContinueWith(TaskThree, TaskContinuationOptions.OnlyOnFaulted);//出現(xiàn)未處理異常情況下繼續(xù)任務(wù)            task1.Start();
        }#endregion

我們看代碼中寫的是先開始運(yùn)行TaskOne(),然后當(dāng)任務(wù)完成后運(yùn)行TaskTwo(Task task) ,如果任務(wù)失敗的話機(jī)會(huì)運(yùn)行TaskThree(Task task)。我們看運(yùn)行結(jié)果中是運(yùn)行了TaskOne()然后成功后運(yùn)行了TaskTwo(Task task),避開了TaskThree(Task task)的運(yùn)行,所以我們是可以通過ContinueWith來進(jìn)行連續(xù)任務(wù)和TaskContinuationOptions進(jìn)行控制任務(wù)運(yùn)行的。

任務(wù)層次—父子層次結(jié)構(gòu)

這里我們利用任務(wù)的連續(xù)性,我就就可以實(shí)現(xiàn)在一個(gè)任務(wù)結(jié)束后立即開啟另一個(gè)任務(wù),任務(wù)也可以構(gòu)成一個(gè)層次結(jié)構(gòu)。就比如一個(gè)任務(wù)中啟動(dòng)了一個(gè)任務(wù),這樣的情況就形成了父子層次的結(jié)構(gòu)。下面我們看的案例就是這么一個(gè)案例。

        #region 任務(wù)的層次結(jié)構(gòu)——父子層次結(jié)構(gòu)public static void ChildTask()
        {
            Console.WriteLine("當(dāng)前運(yùn)行的子任務(wù),開啟");
            Task.Delay(5000).Wait();
            Console.WriteLine("子任務(wù)運(yùn)行結(jié)束");
        }public static void ParentTask()
        {
            Console.WriteLine("父級(jí)任務(wù)開啟");var child = new Task(ChildTask);
            child.Start();
            Task.Delay(1000).Wait();
            Console.WriteLine("父級(jí)任務(wù)結(jié)束");
        }public static void ParentAndChildTask()
        {var parent = new Task(ParentTask);
            parent.Start();
            Task.Delay(2000).Wait();
            Console.WriteLine($"父級(jí)任務(wù)的狀態(tài) :{parent.Status}");
            Task.Delay(4000).Wait();
            Console.WriteLine($"父級(jí)任務(wù)的狀態(tài) :{parent.Status}");
        }#endregion

等待任務(wù)

  在前面問介紹的.Net異步編程中我們講到了WhenAll,用于處理多個(gè)異步方法。在這里我們繼續(xù)擴(kuò)展點(diǎn),WhenAll()和WaitAll(),都是等待傳遞給他們的任務(wù)完成。但是WaitAll()方法阻塞調(diào)用任務(wù),知道所有任務(wù)完成為止,而WhenAll()返回了一個(gè)任務(wù),從而可以使用async關(guān)鍵在等待結(jié)果。不會(huì)阻塞任務(wù)。與之相對(duì)應(yīng)的也還有WaitAny()和WhenAn()。等待任務(wù)還有我們一直都用到了的Task.Delay()方法,指定這個(gè)方法放回的任務(wù)前要等待的毫秒數(shù)。

  下面我們看這個(gè)ValueTask等待類型(結(jié)構(gòu)),相對(duì)于Task類來說,ValueTask沒有堆中對(duì)象的開銷。在一般情況下,Task類型的開銷可以被忽略掉,但是在一些特殊情況下,例如方法被調(diào)用千次萬次來看。這種情況ValueTask就變得很適用了。我們看下面這個(gè)案例,使用ValueTask時(shí),在五秒內(nèi)的情況下直接從它的構(gòu)造函數(shù)返回值。如果時(shí)間不在五秒內(nèi)的話就使用真正獲取數(shù)據(jù)的方法。然后我們與使用Task的方法進(jìn)行對(duì)比。這里我們采取十萬條數(shù)據(jù)的測(cè)試對(duì)比。

        #region 等待任務(wù)private static DateTime _time;private static List<string> _data;public static async ValueTask<List<string>> GetStringDicAsync()
        {if (_time >= DateTime.Now.AddSeconds(-5))
            {return await new ValueTask<List<string>>(_data);
            }else{
                (_data, _time) = await GetData();return _data; 
            }
        }public static Task<(List<string> data, DateTime time)> GetData() => 
            Task.FromResult(
                (Enumerable.Range(0, 10).Select(x => $"itemString{x}").ToList(), DateTime.Now));public static async Task<List<string>> GetStringList()
        {
            (_data, _time) = await GetData();return _data;
        }#endregion
        static async Task  Main(string[] args)
        { 
            Stopwatch stopwatch = new Stopwatch();
            stopwatch.Start();
            Console.WriteLine("ValueTask開始");for (int i = 0; i < 100000; i++)
            {               var itemList= await GetStringDicAsync(); 
            }
            Console.WriteLine("ValueTask結(jié)束");
            Console.WriteLine($"ValueTask耗時(shí):{stopwatch.ElapsedMilliseconds}");

            Console.WriteLine();
            Console.WriteLine();

            stopwatch.Restart();
            Console.WriteLine("Task開始");for (int i = 0; i < 100000; i++)
            {var itemList = await GetStringList(); 
            }
            Console.WriteLine("Task結(jié)束");
            Console.WriteLine($"Task耗時(shí):{stopwatch.ElapsedMilliseconds}");
            Console.ReadLine();
        }

  我們看其運(yùn)行結(jié)果,使用Task和ValueTask的運(yùn)行結(jié)果耗時(shí)相差是巨大的。所以在一些特殊情況下使用ValueTask或許會(huì)更加的適用。

總結(jié)

  今天我們介紹了關(guān)于任務(wù)相關(guān)的一些知識(shí)概念。我們結(jié)合上一篇文章我們來梳理一些任務(wù)、線程、多線程、異步、同步、并發(fā)、并行任務(wù)之間的聯(lián)系與關(guān)系吧。

  首先我們看我們這章節(jié)學(xué)習(xí)的任務(wù)、任務(wù)是一個(gè)將要完成的工作單元,那么由誰完成呢?由線程來運(yùn)行這個(gè)任務(wù)。那么關(guān)于多線程呢?多線程應(yīng)該可以說是一個(gè)設(shè)計(jì)概念,用來實(shí)現(xiàn)線程切換的。多線程就可以運(yùn)行多個(gè)任務(wù),但是在并發(fā)中。在同一時(shí)間內(nèi)只能有一個(gè)程序運(yùn)行。只不過線程間切換速度極快,讓它看起來似乎是在同一時(shí)間運(yùn)行了多個(gè)程序。其實(shí)在微觀上講,并發(fā)在任意時(shí)間點(diǎn)只有一個(gè)程序在運(yùn)行,只不過是線程切換速度快。那么怎么達(dá)到切換速度極快呢?這就需要異步了。在線程運(yùn)行的時(shí)候不需要等到它完成結(jié)果再去繼續(xù)其他的線程任務(wù)。也就是可等待的。實(shí)現(xiàn)A運(yùn)行起來不等待其結(jié)果,然后切換到B繼續(xù)運(yùn)行。這樣切換速度就極快了。我們?cè)僮屑?xì)看多線程切換線程似乎成了實(shí)現(xiàn)異步的一種方法手段了。有異步就有同步,同步來說就不需要使用到多線程了,沒必要。反正等到上一個(gè)任務(wù)運(yùn)行完成。就繼續(xù)使用上一個(gè)線程繼續(xù)運(yùn)行。這里都是講的并發(fā)中的情況。那么并行呢?并行可以說不管在微觀還是宏觀上都是可以實(shí)現(xiàn)一個(gè)時(shí)間運(yùn)行多個(gè)程序的。并發(fā)是多個(gè)程序運(yùn)行在一個(gè)處理機(jī)上,但是并行任務(wù)是運(yùn)行在多個(gè)處理機(jī)上。例如實(shí)現(xiàn)四個(gè)任務(wù)并行,那么我們至少需要四個(gè)邏輯處理內(nèi)核的配合才能到達(dá)。

 項(xiàng)目源碼地址


  世界上那些最容易的事情中,拖延時(shí)間最不費(fèi)力。堅(jiān)韌是成功的一大要素,只要在門上敲得夠久夠大聲,終會(huì)把人喚醒的。

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多