在Task上执行长时间运行的I / O方法

时间:2015-08-24 08:38:09

标签: c# .net multithreading wcf

我有一个WCF操作Say Service1.OperationA。此操作必须调用另一个Operartion Service2.OprationB。 OperationB被称为OperationA中的最后一步。 OperationB只返回true或false。

操作B会对数据库执行一些读写操作,这可能需要很长时间。从操作B调用此方法时,我不想等待OperationB的结果。即使抛出异常,我也会捕获并记录异常。还有其他操作,如OperationC,可能要等待OperationB的结果。

  1. 我可以使用Task在OperationC中运行OperationB,因为它需要很长时间。
  2. 可以在Task上调用I / O操作吗?

2 个答案:

答案 0 :(得分:2)

最简单的方法是使用async-await。

  • 如果您的程序需要一些时间,请声明程序async
  • 而不是返回void返回任务,而不是TResult返回任务<TResult&gt;
  • 在此过程中,以正常的同步方式执行您想要执行的操作。
  • 每当打电话给可能需要一些时间的东西时,请寻找一个等待它的版本。通常,此函数的扩展名为async。
  • 大多数I / O函数都有异步版本。与TextWriter.WriteAsync一样

在异步函数中,只需调用另一个异步函数即可。如果您需要结果,请立即使用await,如果您还有其他事情要做,请在执行异步功能时执行此操作,并在需要结果时等待任务。

让我们假设你的慢速OperationB下载完整的莎士比亚。作为异步函数,这将是:

private async Task<string> OparationBAsync()
{   // read Shakespeare sonnets:
    string uri = "http://www.gutenberg.org/cache/epub/1041/pg1041.txt";
    using (var webClient = new WebClient())
    {
        return await webClient.DownloadStringTaskAsync(uri);
    }
}

如果OperationA是异步的并且您想等待OperationB的结果,则等待结果:

private async Task OperationAAsync(...)
{
    Task<string> taskReadBook = OperationB();
    // while reading do some other useful stuff
    // when you need the result: await for the task:
    string book = await taskReadBook;
    ProcessBook (book);
}

当然,如果您不需要结果,请不要等待它。调用另一个异步函数,并在准备好后返回。您可能会收到编译器警告,表示您没有等待,但这不是您想要的吗?

  • 异步函数只能由其他异步函数调用
  • 每个异步函数都返回任务或任务<TResult&gt;
  • 有一个例外:事件处理程序可能返回void

private async void Button1_Clicked(object sender, ...)
{
    OperationA();
    // use the synchronous version or
    // use the async version and wait until finished:
    await OperationAAsync();
    OperationBAsync();
    // do not await until finished.
}

问题是,如果你想对结果做一些事情,没有人会知道OperationBAsync已经完成。假设您有一个写入读取数据的operationCAsync:

private async Task OperationCAsync(string filename, string text)
{
    using (var textWriter = new StreamWriter(fileName))
    {
        await WriteAsync(text);
    }
 }

最简单的方法是调用函数等待:

private async void Button1_Clicked(object sender, ...)
{
    await OperationAAsync();
    var book = await OperationBAsync();
    await OperationC("mybook.txt", book);
}

即使您没有显式启动任务,异步也会使您的UI在等待函数期间保持响应。请注意,它仍然是执行处理的UI线程,因此在代码未等待时,您的UI线程将忙碌。如果你真的想从这个负担中释放UI,你需要启动一个单独的线程:

private void Button1_Clicked(object sender, ...)
{   // not async version, the UI will not wait for the result
    Task.Run( () => Button1ClickedHandler();       
}

private async Task Button1ClickedHandler()
{
    await OperationA();
    var book = await OperationB();
    await OperationC("mybook.txt", book);
}

当然,您必须注意不再由UI线程执行Button1ClickedHandler,因此它无法触及任何UI项目(按钮,图形等)您还必须考虑如果要做什么,如果当动作仍然忙时,单击该按钮。

最后一个巧妙的技巧,你可以开始几个任务,并等到所有任务完成:

private async Task StartSeveralTasks()
{
    var taskA = OperationA(),
    var taskB = OperationB(),
    var taskC = OperationC(...),
    };
    await Task.WhenAll(new Task[] {taskA, taskB, taskC});
    // because taskB is a Task<string>, taskB has a property Result:
    string book = taskB.Result;
}

Task.WhenAll包装System.AggregateException中任何任务抛出的所有异常。该类有一个属性InnerExceptions,它包含所有已发生异常的序列。

答案 1 :(得分:0)

您可以使用@media screen and (device-width: 360px) and (device-height: 640px) and (-webkit-device-pixel-ratio: 3) 在不同的线程中执行OperationB。像:

Task.Run()