簡體   English   中英

鏈接`任務 <T> 動態地並且不阻塞線程

[英]Chaining `Task<T>` dynamically and without blocking the thread

我正在嘗試像在JavaScript中那樣在C#中鏈接Task<T>對象,並且不阻塞UI線程。

我看到有一個類似的問題在這里 ,但它采用了非通用Task對象的過程函數的返回類型。 我嘗試對Task<T>做同樣的事情。

我也看到, 這里是我的需要仔細一問,但接受的答案似乎使用.Result兩次,我猜會阻塞UI線程。 另外,請注意,我是動態鏈接任務的,因此無法遵循一些簡單的解決方法。 而且, 這里給出的Then實現似乎也是同步的(我不確定是否僅更改此舊示例代碼上的TaskContinuationOptions就可以實現我想要的功能)。

這是我現在擁有的,但是即使不阻塞線程也無法編譯它:

    // Initial dummy task.
    private Task<bool> taskChain = Task.Factory.StartNew<bool>(() => true);

    // Chain dynamically on button click.
    private async void DoSth_Click(object sender, RoutedEventArgs e)
    {
        var data = ....;
        System.Threading.Tasks.Task<bool> del = async (t) => { return await ConvertAsync(data); };
        taskChain = taskChain.ContinueWith<bool>(() => del);
        var res = await taskChain;
    }

我嘗試了各種不同的方法,但是我看不到如何將Task<T>變成Func<Task<T>, T>ContinueWith<bool>()似乎需要(至少沒有做一些討厭的UI線程)阻止操作)。

我希望這很容易,但是我在這里看不到解決方案……難道沒有一個簡便的方法嗎?

(注意:我想我應該在ContinueWith()之后調用Unwrap() ,但是這似乎是一個細節……)

UnWrap是您的朋友在這里。 它可以讓您擁有一個可以解析為Task的延續方法,然后在延續被觸發之前獲得一個代表該任務的Task

還要注意, FromResult應該用於創建已經完成的任務。

private Task<bool> taskChain = Task.FromResult(true);
private async void DoSth_Click(object sender, RoutedEventArgs e)
{
    var data = CreateData();
    taskChain = taskChain.ContinueWith(t => ConvertAsync(data))
        .Unwrap();
    var res = await taskChain;
}

請注意,我建議不要在點擊處理程序中進行此操作。 創建一個類,它是能夠排隊的任務,然后使用 當然,這種隊列類也遵循以下相同的模式:

public class TaskQueue
{
    private Task previous = Task.FromResult(false);
    private object key = new object();

    public Task<T> Enqueue<T>(Func<Task<T>> taskGenerator)
    {
        lock (key)
        {
            var next = previous.ContinueWith(t => taskGenerator()).Unwrap();
            previous = next;
            return next;
        }
    }
    public Task Enqueue(Func<Task> taskGenerator)
    {
        lock (key)
        {
            var next = previous.ContinueWith(t => taskGenerator()).Unwrap();
            previous = next;
            return next;
        }
    }
}

這將使您可以編寫:

private TaskQueue taskQueue = new TaskQueue();
private async void DoSth_Click(object sender, RoutedEventArgs e)
{
    var data = CreateData();
    var res = await TaskQueue.Enqueue(ConvertAsync(data));
}

現在,您的排隊任務機制與該單擊處理程序需要執行的業務邏輯分離。

最簡單的“連鎖”方式是await

// Initial dummy task.
private Task taskChain = Task.FromResult(true);

// Chain dynamically on button click.
private async void DoSth_Click(object sender, RoutedEventArgs e)
{
  var data = ....;
  taskChain = ChainedConvertAsync(taskChain, data);
  var res = await taskChain;
  ...
}

private async Task<Result> ChainedConvertAsync(Task precedent, Data data)
{
  await precedent;
  return await ConvertAsync(data);
}

另外,請避免StartNewContinueWith 由於它們的默認調度程序,它們是危險的API。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM