欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C#多線程系列之任務(wù)基礎(chǔ)(三)

 更新時(shí)間:2022年02月14日 10:28:58   作者:癡者工良  
本文詳細(xì)講解了C#多線程的任務(wù)基礎(chǔ),文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下

TaskAwaiter

先說(shuō)一下 TaskAwaiter,TaskAwaiter 表示等待異步任務(wù)完成的對(duì)象并為結(jié)果提供參數(shù)。

Task 有個(gè) GetAwaiter() 方法,會(huì)返回TaskAwaiter 或TaskAwaiter<TResult>TaskAwaiter 類(lèi)型在 System.Runtime.CompilerServices 命名空間中定義。

TaskAwaiter 類(lèi)型的屬性和方法如下:

屬性:

屬性說(shuō)明
IsCompleted獲取一個(gè)值,該值指示異步任務(wù)是否已完成。

方法:

方法說(shuō)明
GetResult()結(jié)束異步任務(wù)完成的等待。
OnCompleted(Action)將操作設(shè)置為當(dāng) TaskAwaiter 對(duì)象停止等待異步任務(wù)完成時(shí)執(zhí)行。
UnsafeOnCompleted(Action)計(jì)劃與此 awaiter 相關(guān)異步任務(wù)的延續(xù)操作。

使用示例如下:

        static void Main()
        {
            Task<int> task = new Task<int>(()=>
            {
                Console.WriteLine("我是前驅(qū)任務(wù)");
                Thread.Sleep(TimeSpan.FromSeconds(1));
                return 666;
            });

            TaskAwaiter<int> awaiter = task.GetAwaiter();

            awaiter.OnCompleted(()=>
            {
                Console.WriteLine("前驅(qū)任務(wù)完成時(shí),我就會(huì)繼續(xù)執(zhí)行");
            });
            task.Start();

            Console.ReadKey();
        }

另外,我們前面提到過(guò),任務(wù)發(fā)生未經(jīng)處理的異常,任務(wù)被終止,也算完成任務(wù)。

延續(xù)的另一種方法

上一節(jié)我們介紹了 .ContinueWith() 方法來(lái)實(shí)現(xiàn)延續(xù),這里我們介紹另一個(gè)延續(xù)方法 .ConfigureAwait()。

.ConfigureAwait() 如果要嘗試將延續(xù)任務(wù)封送回原始上下文,則為 true;否則為 false

我來(lái)解釋一下, .ContinueWith() 延續(xù)的任務(wù),當(dāng)前驅(qū)任務(wù)完成后,延續(xù)任務(wù)會(huì)繼續(xù)在此線程上繼續(xù)執(zhí)行。這種方式是同步的,前者和后者連續(xù)在一個(gè)線程上運(yùn)行。

.ConfigureAwait(false) 方法可以實(shí)現(xiàn)異步,前驅(qū)方法完成后,可以不理會(huì)后續(xù)任務(wù),而且后續(xù)任務(wù)可以在任意一個(gè)線程上運(yùn)行。這個(gè)特性在 UI 界面程序上特別有用。

可以參考:https://medium.com/bynder-tech/c-why-you-should-use-configureawait-false-in-your-library-code-d7837dce3d7f

其使用方法如下:

        static void Main()
        {
            Task<int> task = new Task<int>(()=>
            {
                Console.WriteLine("我是前驅(qū)任務(wù)");
                Thread.Sleep(TimeSpan.FromSeconds(1));
                return 666;
            });

            ConfiguredTaskAwaitable<int>.ConfiguredTaskAwaiter awaiter = task.ConfigureAwait(false).GetAwaiter();

            awaiter.OnCompleted(()=>
            {
                Console.WriteLine("前驅(qū)任務(wù)完成時(shí),我就會(huì)繼續(xù)執(zhí)行");
            });
            task.Start();

            Console.ReadKey();
        }

ConfiguredTaskAwaitable<int>.ConfiguredTaskAwaiter 擁有跟 TaskAwaiter 一樣的屬性和方法。

.ContinueWith() 跟 .ConfigureAwait(false) 還有一個(gè)區(qū)別就是 前者可以延續(xù)多個(gè)任務(wù)和延續(xù)任務(wù)的任務(wù)(多層)。后者只能延續(xù)一層任務(wù)(一層可以有多個(gè)任務(wù))。

另一種創(chuàng)建任務(wù)的方法

前面提到提到過(guò),創(chuàng)建任務(wù)的三種方法:new Task()、Task.Run()Task.Factory.SatrtNew(),現(xiàn)在來(lái)學(xué)習(xí)第四種方法:TaskCompletionSource<TResult> 類(lèi)型。

我們來(lái)看看 TaskCompletionSource<TResulr> 類(lèi)型的屬性和方法:

屬性:

屬性說(shuō)明
Task獲取由此 Task 創(chuàng)建的 TaskCompletionSource。

方法:

方法說(shuō)明
SetCanceled()將基礎(chǔ) Task 轉(zhuǎn)換為 Canceled 狀態(tài)。
SetException(Exception)將基礎(chǔ) Task 轉(zhuǎn)換為 Faulted 狀態(tài),并將其綁定到一個(gè)指定異常上。
SetException(IEnumerable)將基礎(chǔ) Task 轉(zhuǎn)換為 Faulted 狀態(tài),并對(duì)其綁定一些異常對(duì)象。
SetResult(TResult)將基礎(chǔ) Task 轉(zhuǎn)換為 RanToCompletion 狀態(tài)。
TrySetCanceled()嘗試將基礎(chǔ) Task 轉(zhuǎn)換為 Canceled 狀態(tài)。
TrySetCanceled(CancellationToken)嘗試將基礎(chǔ) Task 轉(zhuǎn)換為 Canceled 狀態(tài)并啟用要存儲(chǔ)在取消的任務(wù)中的取消標(biāo)記。
TrySetException(Exception)嘗試將基礎(chǔ) Task 轉(zhuǎn)換為 Faulted 狀態(tài),并將其綁定到一個(gè)指定異常上。
TrySetException(IEnumerable)嘗試將基礎(chǔ) Task 轉(zhuǎn)換為 Faulted 狀態(tài),并對(duì)其綁定一些異常對(duì)象。
TrySetResult(TResult)嘗試將基礎(chǔ) Task 轉(zhuǎn)換為 RanToCompletion 狀態(tài)。

TaskCompletionSource<TResulr> 類(lèi)可以對(duì)任務(wù)的生命周期做控制。

首先要通過(guò) .Task 屬性,獲得一個(gè) Task 或 Task<TResult> 。

            TaskCompletionSource<int> task = new TaskCompletionSource<int>();
            Task<int> myTask = task.Task;	//  Task myTask = task.Task;

然后通過(guò) task.xxx() 方法來(lái)控制 myTask 的生命周期,但是呢,myTask 本身是沒(méi)有任務(wù)內(nèi)容的。

使用示例如下:

        static void Main()
        {
            TaskCompletionSource<int> task = new TaskCompletionSource<int>();
            Task<int> myTask = task.Task;       // task 控制 myTask

            // 新開(kāi)一個(gè)任務(wù)做實(shí)驗(yàn)
            Task mainTask = new Task(() =>
            {
                Console.WriteLine("我可以控制 myTask 任務(wù)");
                Console.WriteLine("按下任意鍵,我讓 myTask 任務(wù)立即完成");
                Console.ReadKey();
                task.SetResult(666);
            });
            mainTask.Start();

            Console.WriteLine("開(kāi)始等待 myTask 返回結(jié)果");
            Console.WriteLine(myTask.Result);
            Console.WriteLine("結(jié)束");
            Console.ReadKey();
        }

其它例如 SetException(Exception) 等方法,可以自行探索,這里就不再贅述。

參考資料:https://devblogs.microsoft.com/premier-developer/the-danger-of-taskcompletionsourcet-class/

這篇文章講得不錯(cuò),而且有圖:https://gigi.nullneuron.net/gigilabs/taskcompletionsource-by-example/

實(shí)現(xiàn)一個(gè)支持同步和異步任務(wù)的類(lèi)型

這部分內(nèi)容對(duì) TaskCompletionSource<TResult> 繼續(xù)進(jìn)行講解。

這里我們來(lái)設(shè)計(jì)一個(gè)類(lèi)似 Task 類(lèi)型的類(lèi),支持同步和異步任務(wù)。

  • 用戶(hù)可以使用 GetResult() 同步獲取結(jié)果;
  • 用戶(hù)可以使用 RunAsync() 執(zhí)行任務(wù),使用 .Result 屬性異步獲取結(jié)果;

其實(shí)現(xiàn)如下:

/// <summary>
/// 實(shí)現(xiàn)同步任務(wù)和異步任務(wù)的類(lèi)型
/// </summary>
/// <typeparam name="TResult"></typeparam>
public class MyTaskClass<TResult>
{
    private readonly TaskCompletionSource<TResult> source = new TaskCompletionSource<TResult>();
    private Task<TResult> task;
    // 保存用戶(hù)需要執(zhí)行的任務(wù)
    private Func<TResult> _func;

    // 是否已經(jīng)執(zhí)行完成,同步或異步執(zhí)行都行
    private bool isCompleted = false;
    // 任務(wù)執(zhí)行結(jié)果
    private TResult _result;

    /// <summary>
    /// 獲取執(zhí)行結(jié)果
    /// </summary>
    public TResult Result
    {
        get
        {
            if (isCompleted)
                return _result;
            else return task.Result;
        }
    }
    public MyTaskClass(Func<TResult> func)
    {
        _func = func;
        task = source.Task;
    }

    /// <summary>
    /// 同步方法獲取結(jié)果
    /// </summary>
    /// <returns></returns>
    public TResult GetResult()
    {
        _result = _func.Invoke();
        isCompleted = true;
        return _result;
    }

    /// <summary>
    /// 異步執(zhí)行任務(wù)
    /// </summary>
    public void RunAsync()
    {
        Task.Factory.StartNew(() =>
        {
            source.SetResult(_func.Invoke());
            isCompleted = true;
        });
    }
}

我們?cè)?Main 方法中,創(chuàng)建任務(wù)示例:

    class Program
    {
        static void Main()
        {
            // 實(shí)例化任務(wù)類(lèi)
            MyTaskClass<string> myTask1 = new MyTaskClass<string>(() =>
            {
                Thread.Sleep(TimeSpan.FromSeconds(1));
                return "www.whuanle.cn";
            });

            // 直接同步獲取結(jié)果
            Console.WriteLine(myTask1.GetResult());


            // 實(shí)例化任務(wù)類(lèi)
            MyTaskClass<string> myTask2 = new MyTaskClass<string>(() =>
            {
                Thread.Sleep(TimeSpan.FromSeconds(1));
                return "www.whuanle.cn";
            });

            // 異步獲取結(jié)果
            myTask2.RunAsync();

            Console.WriteLine(myTask2.Result);


            Console.ReadKey();
        }
    }

Task.FromCanceled()

微軟文檔解釋?zhuān)簞?chuàng)建 Task,它因指定的取消標(biāo)記進(jìn)行的取消操作而完成。

這里筆者抄來(lái)了一個(gè)示例

var token = new CancellationToken(true);
Task task = Task.FromCanceled(token);
Task<int> genericTask = Task.FromCanceled<int>(token);

網(wǎng)上很多這樣的示例,但是,這個(gè)東西到底用來(lái)干嘛的?new 就行了?

帶著疑問(wèn)我們來(lái)探究一下,來(lái)個(gè)示例:

        public static Task Test()
        {
            CancellationTokenSource source = new CancellationTokenSource();
            source.Cancel();
            return Task.FromCanceled<object>(source.Token);
        }
        static void Main()
        {
            var t = Test();	// 在此設(shè)置斷點(diǎn),監(jiān)控變量
            Console.WriteLine(t.IsCanceled);
         }

Task.FromCanceled() 可以構(gòu)造一個(gè)被取消的任務(wù)。我找了很久,沒(méi)有找到很好的示例,如果一個(gè)任務(wù)在開(kāi)始前就被取消,那么使用 Task.FromCanceled() 是很不錯(cuò)的。

這里有很多示例可以參考:https://www.csharpcodi.com/csharp-examples/System.Threading.Tasks.Task.FromCanceled(System.Threading.CancellationToken)/

如何在內(nèi)部取消任務(wù)

之前我們討論過(guò),使用 CancellationToken 取消令牌傳遞參數(shù),使任務(wù)取消。但是都是從外部傳遞的,這里來(lái)實(shí)現(xiàn)無(wú)需 CancellationToken 就能取消任務(wù)。

我們可以使用 CancellationToken 的 ThrowIfCancellationRequested() 方法拋出 System.OperationCanceledException 異常,然后終止任務(wù),任務(wù)會(huì)變成取消狀態(tài),不過(guò)任務(wù)需要先傳入一個(gè)令牌。

這里筆者來(lái)設(shè)計(jì)一個(gè)難一點(diǎn)的東西,一個(gè)可以按順序執(zhí)行多個(gè)任務(wù)的類(lèi)。

示例如下:

    /// <summary>
    /// 能夠完成多個(gè)任務(wù)的異步類(lèi)型
    /// </summary>
    public class MyTaskClass
    {
        private List<Action> _actions = new List<Action>();
        private CancellationTokenSource _source = new CancellationTokenSource();
        private CancellationTokenSource _sourceBak = new CancellationTokenSource();
        private Task _task;

        /// <summary>
        ///  添加一個(gè)任務(wù)
        /// </summary>
        /// <param name="action"></param>
        public void AddTask(Action action)
        {
            _actions.Add(action);
        }

        /// <summary>
        /// 開(kāi)始執(zhí)行任務(wù)
        /// </summary>
        /// <returns></returns>
        public Task StartAsync()
        {
            // _ = new Task() 對(duì)本示例無(wú)效
            _task = Task.Factory.StartNew(() =>
             {
                 for (int i = 0; i < _actions.Count; i++)
                 {
                     int tmp = i;
                     Console.WriteLine($"第 {tmp} 個(gè)任務(wù)");
                     if (_source.Token.IsCancellationRequested)
                     {
                         Console.ForegroundColor = ConsoleColor.Red;
                         Console.WriteLine("任務(wù)已經(jīng)被取消");
                         Console.ForegroundColor = ConsoleColor.White;
                         _sourceBak.Cancel();
                         _sourceBak.Token.ThrowIfCancellationRequested();
                     }
                     _actions[tmp].Invoke();
                 }
             },_sourceBak.Token);
            return _task;
        }

        /// <summary>
        /// 取消任務(wù)
        /// </summary>
        /// <returns></returns>
        public Task Cancel()
        {
            _source.Cancel();

            // 這里可以省去
            _task = Task.FromCanceled<object>(_source.Token);
            return _task;
        }
    }

Main 方法中:

        static void Main()
        {
            // 實(shí)例化任務(wù)類(lèi)
            MyTaskClass myTask = new MyTaskClass();

            for (int i = 0; i < 10; i++)
            {
                int tmp = i;
                myTask.AddTask(() =>
                {
                    Console.WriteLine("     任務(wù) 1 Start");
                    Thread.Sleep(TimeSpan.FromSeconds(1));
                    Console.WriteLine("     任務(wù) 1 End");
                    Thread.Sleep(TimeSpan.FromSeconds(1));
                });
            }

            // 相當(dāng)于 Task.WhenAll()
            Task task = myTask.StartAsync();
            Thread.Sleep(TimeSpan.FromSeconds(1));
            Console.WriteLine($"任務(wù)是否被取消:{task.IsCanceled}");

            // 取消任務(wù)
            Console.ForegroundColor = ConsoleColor.Red;
            Console.WriteLine("按下任意鍵可以取消任務(wù)");
            Console.ForegroundColor = ConsoleColor.White;
            Console.ReadKey();

            var t = myTask.Cancel();    // 取消任務(wù)
            Thread.Sleep(TimeSpan.FromSeconds(2));
            Console.WriteLine($"任務(wù)是否被取消:【{task.IsCanceled}】");

            Console.ReadKey();
        }

你可以在任一階段取消任務(wù)。

Yield 關(guān)鍵字

迭代器關(guān)鍵字,使得數(shù)據(jù)不需要一次性返回,可以在需要的時(shí)候一條條迭代,這個(gè)也相當(dāng)于異步。

迭代器方法運(yùn)行到 yield return 語(yǔ)句時(shí),會(huì)返回一個(gè) expression,并保留當(dāng)前在代碼中的位置。 下次調(diào)用迭代器函數(shù)時(shí),將從該位置重新開(kāi)始執(zhí)行。

可以使用 yield break 語(yǔ)句來(lái)終止迭代。

官方文檔:https://docs.microsoft.com/zh-cn/dotnet/csharp/language-reference/keywords/yield

網(wǎng)上的示例大多數(shù)都是 foreach 的,有些同學(xué)不理解這個(gè)到底是啥意思。筆者這里簡(jiǎn)單說(shuō)明一下。

我們也可以這樣寫(xiě)一個(gè)示例:

這里已經(jīng)沒(méi)有 foreach 了。

        private static int[] list = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };

        private static IEnumerable<int> ForAsync()
        {
            int i = 0;
            while (i < list.Length)
            {
                i++;
                yield return list[i];
            }
        }

但是,同學(xué)又問(wèn),這個(gè) return 返回的對(duì)象 要實(shí)現(xiàn)這個(gè) IEnumerable<T> 才行嘛?那些文檔說(shuō)到什么迭代器接口什么的,又是什么東西呢?

我們可以先來(lái)改一下示例:

        private static int[] list = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };

        private static IEnumerable<int> ForAsync()
        {
            int i = 0;
            while (i < list.Length)
            {
                int num = list[i];
                i++;
                yield return num;
            }
        }

你在 Main 方法中調(diào)用,看看是不是正常運(yùn)行?

        static void Main()
        {
            foreach (var item in ForAsync())
            {
                Console.WriteLine(item);
            }
            Console.ReadKey();
        }

這樣說(shuō)明了,yield return 返回的對(duì)象,并不需要實(shí)現(xiàn) IEnumerable<int> 方法。

其實(shí) yield 是語(yǔ)法糖關(guān)鍵字,你只要在循環(huán)中調(diào)用它就行了。

        static void Main()
        {
            foreach (var item in ForAsync())
            {
                Console.WriteLine(item);
            }
            Console.ReadKey();
        }

        private static IEnumerable<int> ForAsync()
        {
            int i = 0;
            while (i < 100)
            {
                i++;
                yield return i;
            }
        }
    }

它會(huì)自動(dòng)生成 IEnumerable<T> ,而不需要你先實(shí)現(xiàn) IEnumerable<T> 。

補(bǔ)充知識(shí)點(diǎn)

  • 線程同步有多種方法:臨界區(qū)(Critical Section)、互斥量(Mutex)、信號(hào)量(Semaphores)、事件(Event)、任務(wù)(Task);

  • Task.Run() 和 Task.Factory.StartNew() 封裝了 Task;

  • Task.Run()是 Task.Factory.StartNew() 的簡(jiǎn)化形式;

  • 有些地方 net Task() 是無(wú)效的;但是 Task.Run() 和 Task.Factory.StartNew() 可以;

到此這篇關(guān)于C#多線程系列之任務(wù)基礎(chǔ)(三)的文章就介紹到這了。希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • C#如何動(dòng)態(tài)設(shè)置屏幕分辨率

    C#如何動(dòng)態(tài)設(shè)置屏幕分辨率

    這篇文章主要為大家詳細(xì)介紹了C#動(dòng)態(tài)設(shè)置屏幕分辨率的方法,我們可以使用Screen類(lèi)設(shè)置屏幕分辨率,感興趣的小伙伴們可以參考一下
    2016-04-04
  • unity實(shí)現(xiàn)鼠標(biāo)拖住3D物體

    unity實(shí)現(xiàn)鼠標(biāo)拖住3D物體

    這篇文章主要為大家詳細(xì)介紹了unity實(shí)現(xiàn)鼠標(biāo)拖住3D物體,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-07-07
  • C#中Dictionary的作用及用法講解

    C#中Dictionary的作用及用法講解

    這篇文章主要介紹了C#中Dictionary的作用及用法講解,本文還對(duì)dictionary類(lèi)用什么接口實(shí)現(xiàn)、Dictionary的基本用法做了講解,需要的朋友可以參考下
    2014-10-10
  • C#與js實(shí)現(xiàn)去除textbox文本框里面重復(fù)記錄的方法

    C#與js實(shí)現(xiàn)去除textbox文本框里面重復(fù)記錄的方法

    這篇文章主要介紹了C#與js實(shí)現(xiàn)去除textbox文本框里面重復(fù)記錄的方法,很實(shí)用的功能,需要的朋友可以參考下
    2014-08-08
  • C#生成比較短的Token字符串

    C#生成比較短的Token字符串

    這篇文章介紹了C#生成Token字符串的方法,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-06-06
  • 一文掌握C# ListView控件的用法和示例代碼

    一文掌握C# ListView控件的用法和示例代碼

    ListView控件提供了豐富的屬性和事件,可以用于實(shí)現(xiàn)各種各樣的表格視圖,包括帶有單元格編輯、排序和分組等功能,本文介紹了一些常見(jiàn)的?ListView?控件的用法和示例代碼,感興趣的朋友一起看看吧
    2024-02-02
  • 算法練習(xí)之從String.indexOf的模擬實(shí)現(xiàn)開(kāi)始

    算法練習(xí)之從String.indexOf的模擬實(shí)現(xiàn)開(kāi)始

    這篇文章主要介紹了算法練習(xí)從String.indexOf的模擬實(shí)現(xiàn)開(kāi)始,需要的朋友可以參考下
    2014-12-12
  • C# 泛型深入理解介紹

    C# 泛型深入理解介紹

    在上一個(gè)專(zhuān)題中介紹了C#2.0 中引入泛型的原因以及有了泛型后所帶來(lái)的好處,然而上一專(zhuān)題相當(dāng)于是介紹了泛型的一些基本知識(shí)的,對(duì)于泛型的性能為什么會(huì)比非泛型的性能高卻沒(méi)有給出理由,所以在這個(gè)專(zhuān)題就中將會(huì)介紹原因和一些關(guān)于泛型的其他知識(shí)
    2012-11-11
  • C#客戶(hù)端程序調(diào)用外部程序的3種實(shí)現(xiàn)方法

    C#客戶(hù)端程序調(diào)用外部程序的3種實(shí)現(xiàn)方法

    這篇文章主要給大家介紹了關(guān)于C#客戶(hù)端程序調(diào)用外部程序的3種實(shí)現(xiàn)方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。
    2018-04-04
  • C#使用Resources資源文件

    C#使用Resources資源文件

    這篇文章介紹了C#使用Resources資源文件的方法,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-06-06

最新評(píng)論