简体   繁体   中英

Continuous Tasks

I am trying to figure out how to properly chain tasks together. So far I have the following:

Task taskExe = new Task(() => startDownload(downloadLink, saveTo));
Task taskVersionTxt = taskExe.ContinueWith((t) => startDownload(versionLink, versionSaveTo));
Task taskWriteVersion = taskVersionTxt.ContinueWith((t) => writeNewVersion());
taskExe.Start();

This doesn't work the way I imagined because writenNewVersion requires that the first 2 tasks be finished so that it can modify the file that was downloaded.

Here is startDownload

private async Task startDownload(string link, string savePath)
{                              
    WebClient client = new WebClient();
    client.DownloadProgressChanged += new DownloadProgressChangedEventHandler(client_DownloadProgressChanged);
    client.DownloadFileCompleted += new AsyncCompletedEventHandler(client_DownloadFileCompleted);
    await client.DownloadFileTaskAsync(new Uri(link), savePath);                    
}

What is the proper way to chain 3 tasks together that wait for the previous task to finish?

If you are able to use async / await , the process is quite straightforward. Start the first two operations to run in parallel, then use await Task.WhenAll(...) to wait for them to both complete.

public async Task RunOperationAsync()
{
    List<Task> downloads = new List<Task>();
    downloads.Add(startDownload(downloadLink, saveTo));
    downloads.Add(startDownload(versionLink, versionSaveTo));
    await Task.WhenAll(downloads);

    await writeNewVersion();
}

startDownload is already asynchronous. Starting it in a new thread is creating a task who's sole job is to start a new task. You would see this more clearly if you looked at the actual types of your variables:

Here is what their real types are:

Task<Task> taskExe = new Task(() => 
    startDownload(downloadLink, saveTo));
Task<Task> taskVersionTxt = taskExe.ContinueWith((t) => 
    startDownload(versionLink, versionSaveTo));
Task<Task> taskWriteVersion = taskVersionTxt.ContinueWith((t) => 
    writeNewVersion());

Your tasks are complete when you finish starting the asynchronous operations , not when those operations actually complete. You need to wait until the wrapped task completes, not the outer task, by Unwrap -ing it. Or, for that matter, just don't wrap the task needlessly in the first place.

Task taskExe = startDownload(downloadLink, saveTo);
Task taskVersionTxt = taskExe.ContinueWith((t) => 
    startDownload(versionLink, versionSaveTo))
    .Unwrap();
Task taskWriteVersion = taskVersionTxt.ContinueWith((t) => 
    writeNewVersion())
    .Unwrap();

Of course, using await is simpler:

await startDownload(downloadLink, saveTo);
await startDownload(versionLink, versionSaveTo);
await writeNewVersion();

The problem is that you're using both tasks and async. If you take async off of your startDownload method, it should work, or if you stop using tasks. You're making it async in 2 different ways, and then only synchronizing one of them, and expecting it to be synchronous.

In LINQPad, this works as expected:

void Main()
{
    string downloadLink = "dl", saveTo = "st", versionLink = "vl", versionSaveTo = "vst";
    Task taskExe = new Task(() => startDownload(downloadLink, saveTo));
    Task taskVersionTxt = taskExe.ContinueWith((t) => startDownload(versionLink, versionSaveTo));
    Task taskWriteVersion = taskVersionTxt.ContinueWith((t) => writeNewVersion());
    taskExe.Start();
}

void startDownload(string dl, string st)
{
    Thread.Sleep(1000);
    ("Download done: " + dl + " " + st).Dump();
}

void writeNewVersion()    
{
    "Version done".Dump();
}
public async Task RunOperationAsync()
{
    await Task.WhenAll(
        startDownload(downloadLink, saveTo),
        startDownload(versionLink, versionSaveTo));

    await writeNewVersion();
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM