Skip to content

C# 多线程 04-使用任务平行库 09-使用 TaskScheduler 配置任务的执行

🏷️ 《C# 多线程》

使用 TaskScheduler 配置任务的执行

本节新建了一个 WPF 项目,用以观察异步时界面的响应效果。

页面代码

xml
<Window x:Class="Recipe4_9.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:Recipe4_9"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <TextBlock
            Name="ContentTextBlock"
            HorizontalAlignment="Left"
            Margin="44,134,0,0" 
            VerticalAlignment="Top"
            Width="425"
            Height="40" />
        
        <Button
            Content="Sync"
            HorizontalAlignment="Left"
            Margin="45,190,0,0" 
            VerticalAlignment="Top"
            Width="75"
            Click="ButtonSync_Click"/>
        
        <Button
            Content="Async"
            HorizontalAlignment="Left"
            Margin="165,190,0,0"
            VerticalAlignment="Top"
            Width="75"
            Click="ButtonAsync_Click"/>

        <Button
            Content="Async OK"
            HorizontalAlignment="Left"
            Margin="285,190,0,0"
            VerticalAlignment="Top"
            Width="75"
            Click="ButtonAsyncOK_Click"/>

    </Grid>
</Window>

后台代码

csharp
public MainWindow()
{
    InitializeComponent();
}

private void ButtonSync_Click(object sender, RoutedEventArgs e)
{
    ContentTextBlock.Text = string.Empty;
    try
    {
        // string result = TaskMethod(TaskScheduler.FromCurrentSynchronizationContext()).Result;
        // 同步调用,当前用户界面会被冻结,在任务执行完毕前无法响应任何操作
        // TaskMethod 中的 Task 线程无法 UI 线程中的控件 ContentTextBlock,导致发生异常
        string result = TaskMethod().Result;
        // 该赋值不会被执行到
        ContentTextBlock.Text = result;
    }
    catch (Exception ex)
    {
        // 出异常时显示异常消息
        ContentTextBlock.Text = ex.InnerException.Message;
    }
}

private void ButtonAsync_Click(object sender, RoutedEventArgs e)
{
    ContentTextBlock.Text = string.Empty;
    Mouse.OverrideCursor = Cursors.Wait;
    // 异步执行 Task,用户界面不会被冻结,任务执行期间界面仍然可以响应用户操作
    // 但是仍然会发生异常
    Task<string> task = TaskMethod();
    task.ContinueWith(
        t =>
        {
            ContentTextBlock.Text = t.Exception.InnerException.Message;
            Mouse.OverrideCursor = null;
        },
        CancellationToken.None,
        TaskContinuationOptions.OnlyOnFaulted,
        TaskScheduler.FromCurrentSynchronizationContext());
}

private void ButtonAsyncOK_Click(object sender, RoutedEventArgs e)
{
    ContentTextBlock.Text = string.Empty;
    Mouse.OverrideCursor = Cursors.Wait;

    // 将 UI 线程任务调度程序 (TaskScheduler.FromCurrentSynchronizationContext()) 提供给了任务
    Task<string> task = TaskMethod(TaskScheduler.FromCurrentSynchronizationContext());
    task.ContinueWith(
        t => Mouse.OverrideCursor = null,
        CancellationToken.None,
        TaskContinuationOptions.None,
        TaskScheduler.FromCurrentSynchronizationContext());
}

private Task<string> TaskMethod()
{
    return TaskMethod(TaskScheduler.Default);
}

private Task<string> TaskMethod(TaskScheduler scheduler)
{
    Task delay = Task.Delay(TimeSpan.FromSeconds(5));
    return delay.ContinueWith(t =>
    {
        string str = $"Task is running on a thread id {Thread.CurrentThread.ManagedThreadId}. " +
        $"Is thread pool thread: {Thread.CurrentThread.IsThreadPoolThread}";
        
        ContentTextBlock.Text = str;
        return str;
    }, scheduler);
}

执行结果

  1. 单击 Sync 按钮:界面无法响应任何操作,异步处理发生异常

  2. 单击 Async 按钮:界面可以响应操作,但是异步处理仍然发生异常

  3. 单击 Async OK 按钮:界面可以响应操作,异步处理没有发生异常