如何从 WPF gui 运行异步任务并与之交互 [英] How to run and interact with an async Task from a WPF gui

查看:16
本文介绍了如何从 WPF gui 运行异步任务并与之交互的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个 WPF GUI,我想在其中按下一个按钮来开始一个长时间的任务,而不会在任务期间冻结窗口.当任务正在运行时,我想获得进度报告,并且我想加入另一个按钮,可以在我选择的任何时间停止任务.

我无法找到使用 async/await/task 的正确方法.我无法包括我尝试过的所有内容,但这是我目前所拥有的.

一个 WPF 窗口类:

公共部分类 MainWindow : Window{只读 otherClass _burnBabyBurn = new OtherClass();内部 bool StopWorking = false;//一个按钮方法启动长时间运行的方法私有异步无效Button_Click_3(对象发送者,RoutedEventArgs e){任务slowBurn = _burnBabyBurn.ExecuteLongProcedureAsync(this, intParam1, intParam2, intParam3);等待slowBurn;}//一个按钮方法来中断和停止长时间运行的方法私有无效StopButton_Click(对象发送者,RoutedEventArgs e){停止工作 = 真;}//允许worker方法回调更新gui的方法内部 void UpdateWindow(字符串消息){TextBox1.Text = 消息;}}

还有一个用于工作方法的类:

class OtherClass{内部任务 ExecuteLongProcedureAsync(MainWindow gui, int param1, int param2, int param3){var tcs = new TaskCompletionSource();//开始工作gui.UpdateWindow(工作开始");虽然(仍在工作){//中程进度报告gui.UpdateWindow(洗澡水 n% 被扔掉了");if (gui.StopTraining) 返回 tcs.Task;}//退出消息gui.UpdateWindow(完成并完成");返回 tcs.Task;}}

这会运行,但是一旦工作方法启动,WPF 函数窗口仍然被阻塞.

我需要知道如何安排 async/await/task 声明以允许

A) 不阻塞gui窗口的worker方法
B) 让worker方法更新gui窗口
C) 允许gui窗口停止中断并停止worker方法

非常感谢任何帮助或指示.

解决方案

长话短说:

private async void ButtonClickAsync(object sender, RoutedEventArgs e){//在 UI 线程中修改 UI 对象txt.Text = "开始";//在另一个线程中运行一个方法等待 HeavyMethodAsync(txt);//<<方法执行到此结束>>//在 UI 线程中修改 UI 对象txt.Text =完成";}//这是一个线程安全的方法.您可以在任何线程中运行它内部异步任务 HeavyMethodAsync(TextBox textBox){同时(仍在工作){textBox.Dispatcher.Invoke(() =>{//UI 操作进入 Invoke 内部textBox.Text += ".";//注意://Dispatcher.Invoke() 无论如何都会阻塞 UI 线程//但没有它你不能从另一个线程修改 UI 对象});//受 CPU 限制或受 I/O 限制的操作在 Invoke 之外//await 不会阻塞 UI 线程,除非它在同步上下文中运行等待 Task.Delay(51);}}

结果:开始………………完成


您需要了解(1)如何编写async代码(2)如何在另一个线程中运行UI操作以及(3)如何取消任务.

我不会在这篇文章中讨论 (3) 取消机制.只要知道您可以创建一个 CancellationTokenSource,它会为您提供一个 CancellationToken,您可以将其传递给任何方法.你取消源,所有令牌都会知道.


asyncawait:

asyncawait 的基础知识

  1. 您只能在 async 方法中await.

  2. 你只能await一个可等待的对象(即TaskValueTaskTask>、IAsyncEnumerable 等)这些对象环绕 async 方法的返回类型,并且 await 关键字将它们展开.(参见包装和展开部分)

  3. 异步方法名称应该总是Async结尾,以提高可读性并防止错误.

    //同步方法:TResult MethodName(params) { }//异步方法:异步任务MethodNameAsync(params) { }

asyncawait

的神奇之处

  1. async-await 语法特性,使用状态机让编译器放弃收回控制权async 方法中的 async 任务.

  2. 执行在 await 等待任务完成并返回结果,不阻塞主线程.

  3. Task.Run线程池 中将 Task 排队.(除非它是一个操作.)async 方法不会在另一个线程中运行.asyncawait 本身与线程创建无关.

所以

当您运行Task(例如Task.Run(action))时,您(重新)使用线程来执行该操作.您可以将该任务放在 async 方法中以控制其流程.通过将 async 放在方法签名中,您可以告诉编译器使用状态机来控制该方法的流程(这根本不意味着线程化).通过 await 任务,您可以防止该方法中的执行流程越过 awaited 语句而不会阻塞 UI 线程.如果你想将流程传递给调用者,那么 async 方法本身可以成为一个 Task 这样你就可以将相同的模式级联到调用者中,所以第四:

async Task Caller() { await Method();}异步任务方法(){等待内部();}async Task Inner() { await Task.Run(action);}

事件处理程序类似于下面的代码.

解释了 ExecuteLongProcedure(情况 1 和 2)和 MyButton_ClickAsync(情况 A 和 B)签名中异步存在的两种可能情况:

private async void MyButton_ClickAsync(object sender, RoutedEventArgs e){//将一个任务排队到线程池上运行//1. 如果 ExecuteLongProcedure 是一个普通方法并返回 void任务 task = Task.Run(()=>ExecuteLongProcedure(this, intParam1, intParam2, intParam3));//或者//2. 如果 ExecuteLongProcedure 是一个异步方法并返回 Task任务 task = ExecuteLongProcedureAsync(this, intParam1, intParam2, intParam3);//无论哪种方式 ExecuteLongProcedure 在这里异步运行//如果不等待任务完成,该方法将退出//A. 等待而不阻塞主线程//->要求 MyButton_ClickAsync 是异步的等待任务;//或者//B. 等待并阻塞线程(完全不推荐)//->不要求 MyButton_ClickAsync 是异步的任务.等待();}


异步方法返回类型:

假设您有以下声明:

私有异步 ReturnType MethodAsync() { ... }

  • 如果 ReturnTypeTask 那么 await MethodAsync(); 返回 void

  • 如果 ReturnTypeTask 那么 await MethodAsync(); 返回一个 T

    这称为展开,请参阅下一节(包装和展开).

  • 如果ReturnTypevoid 你不能await

    • 如果你尝试编写 await MethodAsync();,你会得到一个编译错误:
    <块引用>

    不能等待无效

    • 您可以触发后忘记,即只需正常调用方法:MethodAsync();,然后继续您的生活.
    • MethodAsync 执行将是同步的,但是由于它具有 async,它将允许您利用魔法,即您可以编写 await 任务 用于控制执行流程的方法.
    • 这就是 WPF 处理按钮单击事件处理程序的方式,显然是因为您的事件处理程序返回 void.
<块引用>

异步方法的返回类型必须是voidTaskTask,类任务类型,IAsyncEnumerableIAsyncEnumerator


包装和拆开:

包装:

async 方法将它们的返回值包装在一个 Task 中.

例如,此方法将 Task 包裹在 int 周围并返回:

//异步任务私有异步任务GetOneAsync(){int val = 等待CalculateStuffAsync();返回值;//返回一个整数}

展开:

要检索或解包包裹Task<>中的值:

  • 异步选项:await
  • 同步选项:task.Resulttask.GetAwaiter().GetResult()task.WaitAndUnwrapException() 或读取 如何从C#中的同步方法?

例如awaitintTask 中解包出来:

Task任务 = GetOneAsync();int number = 等待任务;//int<-任务<int>

包装和展开的不同方式:

私有任务获取号码(){任务任务;任务 = Task.FromResult(1);//包装准原子操作的正确方法,GetNumber 方法不是异步的任务 = Task.Run(() => 1);//不是包装数字的最佳方式返回任务;}私有异步任务GetNumberAsync(){int number = await Task.Run(GetNumber);//从 Task 中解包 int//不好的做法://int number = Task.Run(GetNumber).GetAwaiter().GetResult();//通过异步同步//int number = Task.Run(GetNumber).Result;//通过异步同步//int number = Task.Run(GetNumber).Wait();//通过异步同步退货号码;//将 int 包装在 Task 中}

还是一头雾水?在 MSDN<上阅读异步返回类型/a>.

<块引用>

要解包任务结果,始终尝试使用 await 而不是 .Result 否则将没有异步优势,只有异步劣势.后者称为异步同步".

注意:

await 是异步的,与同步的 task.Wait() 不同.但他们都做同样的事情,就是等待任务完成.

await 是异步的,与同步的 task.Result 不同.但它们都做同样的事情,即等待任务完成并解包并返回结果.

要获得包装值,您始终可以使用 Task.FromResult(1) 而不是使用 Task.Run(() => 1) 创建新线程代码>.

Task.RunTask.Factory.StartNew

的更新 (.NetFX4.5) 和更简单的版本

WPF 图形界面:

这是我解释如何在另一个线程中运行 UI 操作的地方.

阻止:

关于 WPF 异步事件处理程序,您首先需要了解的是 Dispatcher 将提供一个 同步上下文.此处说明

CPU 绑定或 IO 绑定操作,例如 Sleeptask.Wait()阻塞和消耗线程,即使它们在带有 async 关键字的方法中调用.但是 await Task.Delay() 告诉状态机停止线程上的执行流程,这样它就不会消耗它;意味着线程资源可以在别处使用:

private async void Button_Click(object sender, RoutedEventArgs e){Thread.Sleep(1000);//停止、阻塞和消耗线程池资源await Task.Delay(1000);//停止而不消耗线程池资源Task.Run(() => Thread.Sleep(1000));//不停止但消耗线程池资源await Task.Run(() => Thread.Sleep(1000));//字面上最糟糕的事情}

线程安全:

如果您必须异步访问 GUI(在 ExecuteLongProcedure 方法中),调用任何涉及修改任何非线程安全对象的操作.例如,必须使用与 GUI 线程关联的 Dispatcher 对象调用任何 WPF GUI 对象:

void UpdateWindow(string text){//安全调用Dispatcher.Invoke(() =>{txt.Text += 文本;});}

但是,如果任务是由于来自 ViewModel 的属性更改回调而启动的,则不需要使用 Dispatcher.Invoke 因为回调实际上是从 UI 线程执行.

<块引用>

在非 UI 线程上访问集合

<块引用>

WPF 使您能够访问和修改除创建集合的线程之外的线程上的数据集合.这使您能够使用后台线程从外部源(例如数据库)接收数据,并在 UI 线程上显示数据.通过使用另一个线程来修改集合,您的用户界面可以保持对用户交互的响应.

由 INotifyPropertyChanged 触发的值更改会自动编组回调度程序.

如何开启跨线程访问

记住,async 方法本身运行在主线程上.所以这是有效的:

private async void MyButton_ClickAsync(object sender, RoutedEventArgs e){txt.Text = "开始";//界面线程等待 Task.Run(()=> ExecuteLongProcedure1());txt.Text = "等待";//界面线程等待 Task.Run(()=> ExecuteLongProcedure2());txt.Text = "完成";//界面线程}

从 UI 线程调用 UI 操作的另一种方法是使用 SynchronizationContext,如此处所述.SynchronizationContext 是比 Dispatcher 更强的抽象,并且它是跨平台的.

var uiContext = SynchronizationContext.Current;同时(仍在工作){uiContext.Post(o =>{textBox.Text += ".";}, 空值);等待 Task.Delay(51);}

模式:

即发即弃模式:

出于显而易见的原因,这就是您的 WPF GUI 事件处理程序(例如 Button_ClickAsync)的调用方式.

void Do(){//CPU-Bound 或 IO-Bound 操作}async void DoAsync()//返回 void{等待 Task.Run(Do);}void FireAndForget()//不是阻塞,不是等待{DoAsync();}

开火并观察:

任务返回方法更好,因为未处理的异常会触发 TaskScheduler.UnobservedTaskException.

void Do(){//CPU-Bound 或 IO-Bound 操作}异步任务 DoAsync()//返回任务{等待 Task.Run(Do);}void FireAndWait()//不是阻塞,不是等待{Task.Run(DoAsync);}

在浪费线程资源的同时同步触发和等待:

这被称为异步同步,它是一种同步操作,但它使用多个线程,这可能会导致饥饿.当您在任务完成之前调用 Wait() 或尝试直接从 task.Result 读取结果时,就会发生这种情况.

(避免这种模式)

void Do(){//CPU-Bound 或 IO-Bound 操作}异步任务 DoAsync()//返回任务{等待 Task.Run(Do);}void FireAndWait()//阻塞、等待并使用另外 2 个线程.哎呀!{var task = Task.Run(DoAsync);任务.等待();}


仅此而已吗?

没有.关于async、它的上下文和它的延续,还有很多东西需要学习.特别推荐这篇博文.

任务使用线程?你确定吗?

不一定.阅读这个答案以了解更多关于async的真实面目.

Stephen Cleary 解释了 async-await 完美.他还在他的其他博文中解释了何时有不涉及线程.

阅读更多

ValueTask 和 Task

MSDN解释Task

MSDN 解释async

how-to-call-asynchronous-method-来自同步方法

async await - 幕后

async await - 常见问题

确保您了解异步、并行和并发之间的区别.

您还可以阅读一个简单的异步文件编写器以了解您应该在何处并发.

调查 并发命名空间

最后,阅读这本电子书:Patterns_of_Parallel_Programming_CSharp

I have a WPF GUI, where I want to press a button to start a long task without freezing the window for the duration of the task. While the task is running I would like to get reports on progress, and I would like to incorporate another button that will stop the task at any time I choose.

I cannot figure the correct way to use async/await/task. I can't include everything I've tried, but this is what I have at the moment.

A WPF window class :

public partial class MainWindow : Window
{
    readonly otherClass _burnBabyBurn = new OtherClass();
    internal bool StopWorking = false;
        
    //A button method to start the long running method
    private async void Button_Click_3(object sender, RoutedEventArgs e)
    {   
        Task slowBurn = _burnBabyBurn.ExecuteLongProcedureAsync(this, intParam1, intParam2, intParam3);
            
        await slowBurn;
    }
        
    //A button Method to interrupt and stop the long running method
    private void StopButton_Click(object sender, RoutedEventArgs e)
    {
        StopWorking = true;
    }

    //A method to allow the worker method to call back and update the gui
    internal void UpdateWindow(string message)
    {
        TextBox1.Text = message;
    }
}
 

And a class for the worker method:

class OtherClass
{
    internal Task ExecuteLongProcedureAsync(MainWindow gui, int param1, int param2, int param3)
    {       
        var tcs = new TaskCompletionSource<int>();       
             
        //Start doing work
        gui.UpdateWindow("Work Started");        
             
        While(stillWorking)
        {
        //Mid procedure progress report
        gui.UpdateWindow("Bath water n% thrown out");        
        if (gui.StopTraining) return tcs.Task;
        }
             
        //Exit message
        gui.UpdateWindow("Done and Done");       
        return tcs.Task;        
    }
}

This runs, but the WPF function window is still blocked once the worker method starts.

I need to know how to arrange the async/await/task declarations to allow

A) the worker method to not block the gui window
B) let the worker method update the gui window
C) allow the gui window to stop interrupt and stop the worker method

Any help or pointers are much appreciated.

解决方案

Long story short:

private async void ButtonClickAsync(object sender, RoutedEventArgs e)
{
    // modify UI object in UI thread
    txt.Text = "started";

    // run a method in another thread
    await HeavyMethodAsync(txt);
    // <<method execution is finished here>>

    // modify UI object in UI thread
    txt.Text = "done";
}

// This is a thread-safe method. You can run it in any thread
internal async Task HeavyMethodAsync(TextBox textBox)
{
    while (stillWorking)
    {
        textBox.Dispatcher.Invoke(() =>
        {
            // UI operation goes inside of Invoke
            textBox.Text += ".";
            // Note that: 
            //    Dispatcher.Invoke() blocks the UI thread anyway
            //    but without it you can't modify UI objects from another thread
        });
        
        // CPU-bound or I/O-bound operation goes outside of Invoke
        // await won't block UI thread, unless it's run in a synchronous context
        await Task.Delay(51);
    }
}

Result:
started....................done


You need to know about (1) how to write async code (2) how to run UI operations in another thread and (3) how to cancel a task.

I'm not getting into (3) cancellation mechanism in this post. Just know that you can create a CancellationTokenSource, which gives you a CancellationToken which you can pass into any method. You cancel the source, all tokens will know.


async and await:

Basics of async and await

  1. You can only await in an async method.

  2. You can only await an awaitable object (i.e. Task, ValueTask, Task<T>, IAsyncEnumerable<T>, etc.) These objects wrap around the return type of an async method and await keyword unwraps them. (see Wrapping and Unwrapping section)

  3. Asynchronous method names should always end with Async to increase readability and to prevent mistakes.

    // Synchronous method:
    TResult MethodName(params) { }
    
    // Asynchronous method:
    async Task<TResult> MethodNameAsync(params) { }
    

The magic of async and await

  1. The async-await syntactic feature, uses a state-machine to let the compiler give up and take back the control over the awaited Task in an async method.

  2. The execution waits at await for the task to finish and returns back its results, without blocking the main thread.

  3. Task.Run queues a Task in the thread pool. (Unless the it's a pure operation.) i.e. The async method does not run in another thread. async and await by themselves don't have anything to do with thread creation.

So

When you run a Task (e.g. Task.Run(action)) you (re)use a thread for that action. And you can put that task in an async method to control its flow. By putting async in the method signature you tell the compiler to use state-machine to control the flow of that method (this does not mean threading at all). And by awaiting the task you prevent the execution flow within that method from moving past the awaited statement without blocking UI thread. If you want to pass the flow onto the caller then the async method itself can become a Task so you'll be able to cascade the same pattern out into the caller and so forth:

async Task Caller() { await Method(); }
async Task Method() { await Inner(); }
async Task Inner() { await Task.Run(action); }

The event handler looks like the code below.

Two possible cases for presense of async in the signature of ExecuteLongProcedure (case 1 and 2) and MyButton_ClickAsync (case A and B) are explained:

private async void MyButton_ClickAsync(object sender, RoutedEventArgs e)
{
    //queue a task to run on threadpool

    // 1. if ExecuteLongProcedure is a normal method and returns void
    Task task = Task.Run(()=>
        ExecuteLongProcedure(this, intParam1, intParam2, intParam3)
    );
    // or
    // 2. if ExecuteLongProcedure is an async method and returns Task
    Task task = ExecuteLongProcedureAsync(this, intParam1, intParam2, intParam3);

    // either way ExecuteLongProcedure is running asynchronously here
    // the method will exit if you don't wait for the Task to finish

    // A. wait without blocking the main thread
    //  -> requires MyButton_ClickAsync to be async
    await task;
    // or
    // B. wait and block the thread (NOT RECOMMENDED AT ALL)
    // -> does not require MyButton_ClickAsync to be async
    task.Wait();
}


Async method return types:

Suppose you have the following declaration:

private async ReturnType MethodAsync() { ... }

  • If ReturnType is Task then await MethodAsync(); returns void

  • If ReturnType is Task<T> then await MethodAsync(); returns a value of type T

    This is called Unwrapping, see the next section (Wrapping and Unrwapping).

  • If ReturnType is void you can't await it

    • If you try writing await MethodAsync();, you will get a compile error saying:

    cannot await void

    • You can only fire and forget i.e. just call the method normally: MethodAsync(); and then go on with your life.
    • The MethodAsync execution will be synchronous, however since it has async it will allow you to take advantage of the magic, i.e. you can write await task within the method to control the flow of execution.
    • This is how WPF handles your button click event handler, obviously because your event handler returns void.

The return type of an async method must be void, Task, Task<T>, a task-like type, IAsyncEnumerable<T>, or IAsyncEnumerator<T>


Wrapping and Unrwapping:

Wrapping:

async methods wrap their return values in a Task.

E.g., this method wraps a Task around an int and returns it:

//      async Task<int>
private async Task<int> GetOneAsync()
{
    int val = await CalculateStuffAsync();
    return val;
//  returns an integer
}

Unwrapping:

To retrieve or unwrap the value which is wrapped inside a Task<>:

e.g. await unwraps the int out of the Task:

Task<int> task = GetOneAsync();
int number = await task;
//int     <-       Task<int>

Different ways to wrap and unwrap:

private Task<int> GetNumber()
{
    Task<int> task;

    task = Task.FromResult(1); // the correct way to wrap a quasi-atomic operation, the method GetNumber is not async
    task = Task.Run(() => 1); // not the best way to wrap a number

    return task;
}

private async Task<int> GetNumberAsync()
{
    int number = await Task.Run(GetNumber); // unwrap int from Task<int>

    // bad practices:
    // int number = Task.Run(GetNumber).GetAwaiter().GetResult(); // sync over async
    // int number = Task.Run(GetNumber).Result; // sync over async
    // int number = Task.Run(GetNumber).Wait(); // sync over async

    return number; // wrap int in Task<int>
}

Still confused? Read async return types on MSDN.

To unwrap a task result, Always try to use await instead of .Result otherwise there will be no asynchronous benefit but only asynchronous disadvantages. The latter is called "sync over async".

Note:

await is a asynchronous and is different from task.Wait() which is synchronous. But they both do the same thing which is waiting for the task to finish.

await is a asynchronous and is different from task.Result which is synchronous. But they both do the same thing which is waiting for the task to finish and unwrapping and returning back the results.

To have a wrapped value, you can always use Task.FromResult(1) instead of creating a new thread by using Task.Run(() => 1).

Task.Run is newer (.NetFX4.5) and simpler version of Task.Factory.StartNew


WPF GUI:

This is where I explain how to run UI operations in another thread.

Blocking:

First thing you need to know about WPF async event handlers is that the Dispatcher will provide a synchronization context. Explained here

CPU-bound or IO-bound operations such as Sleep and task.Wait() will block and consume the thread even if they are called in a method with async keyword. but await Task.Delay() tells the state-machine to stop the flow of execution on the thread so it does not consume it; meaning that the thread resources can be used elsewhere:

private async void Button_Click(object sender, RoutedEventArgs e)
{
        Thread.Sleep(1000);//stops, blocks and consumes threadpool resources
        await Task.Delay(1000);//stops without consuming threadpool resources
        Task.Run(() => Thread.Sleep(1000));//does not stop but consumes threadpool resources
        await Task.Run(() => Thread.Sleep(1000));//literally the WORST thing to do
}

Thread Safety:

If you have to access GUI asynchronously (inside ExecuteLongProcedure method), invoke any operation which involves modification to any non-thread-safe object. For instance, any WPF GUI object must be invoked using a Dispatcher object which is associated with the GUI thread:

void UpdateWindow(string text)
{
    //safe call
    Dispatcher.Invoke(() =>
    {
        txt.Text += text;
    });
}

However, If a task is started as a result of a property changed callback from the ViewModel, there is no need to use Dispatcher.Invoke because the callback is actually executed from the UI thread.

Accessing collections on non-UI Threads

WPF enables you to access and modify data collections on threads other than the one that created the collection. This enables you to use a background thread to receive data from an external source, such as a database, and display the data on the UI thread. By using another thread to modify the collection, your user interface remains responsive to user interaction.

Value changes fired by INotifyPropertyChanged are automatically marshalled back onto the dispatcher.

How to enable cross-thread access

Remember, async method itself runs on the main thread. So this is valid:

private async void MyButton_ClickAsync(object sender, RoutedEventArgs e)
{
    txt.Text = "starting"; // UI Thread
    await Task.Run(()=> ExecuteLongProcedure1());
    txt.Text = "waiting"; // UI Thread
    await Task.Run(()=> ExecuteLongProcedure2());
    txt.Text = "finished"; // UI Thread
}

Another way to invoke UI operations from UI thread is to use SynchronizationContext as described here. SynchronizationContext is a stronger abstraction than Dispatcher and it's cross-platform.

var uiContext = SynchronizationContext.Current;
while (stillWorking)
{
    uiContext.Post(o =>
    {
        textBox.Text += ".";
    }, null);
    await Task.Delay(51);
}

Patterns:

Fire and forget pattern:

For obvious reasons this is how your WPF GUI event handlers such as Button_ClickAsync are called.

void Do()
{
    // CPU-Bound or IO-Bound operations
}
async void DoAsync() // returns void
{
    await Task.Run(Do);
}
void FireAndForget() // not blocks, not waits
{
    DoAsync();
}

Fire and observe:

Task-returning methods are better since unhandled exceptions trigger the TaskScheduler.UnobservedTaskException.

void Do()
{
    // CPU-Bound or IO-Bound operations
}
async Task DoAsync() // returns Task
{
    await Task.Run(Do);
}
void FireAndWait() // not blocks, not waits
{
    Task.Run(DoAsync);
}

Fire and wait synchronously while wasting thread resources:

This is known as Sync over async, it is a synchronous operation but it uses more than one thread which may cause starvation. This happens when you call Wait() or try to read results directly from task.Result before the task is finished.

(AVOID THIS PATTERN)

void Do()
{
    // CPU-Bound or IO-Bound operations
}
async Task DoAsync() // returns Task
{
    await Task.Run(Do);
}
void FireAndWait() // blocks, waits and uses 2 more threads. Yikes!
{
    var task = Task.Run(DoAsync);
    task.Wait();
}


Is that all to it?

No. There is a lot more to learn about async, its context and its continuation. This blogpost is especially recommended.

Task uses Thread? Are you sure?

Not necessarily. Read this answer to know more about the true face of async.

Stephen Cleary has explained async-await perfectly. He also explains in his other blog post when there is no thread involved.

Read more

ValueTask and Task

MSDN explains Task

MSDN explains async

how-to-call-asynchronous-method-from-synchronous-method

async await - Behind the scenes

async await - FAQ

Make sure you know the difference between Asynchronous, Parallel and Concurrent.

You may also read a simple asynchronous file writer to know where you should concurrent.

Investigate concurrent namespace

Ultimately, read this e-book: Patterns_of_Parallel_Programming_CSharp

这篇关于如何从 WPF gui 运行异步任务并与之交互的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆