异步调用2个方法

时间:2017-02-27 20:32:52

标签: c# asynchronous async-await task

我不确定我的程序是否完全符合我的要求,或者它是否可以更好地编码。 目标是调用2个方法(CreateHeaderAsync,CombineFilesAsync)并让它们并行工作。

我不认为这是写这些的正确或好方法...... Async()方法,因为它看起来很不洁净。

并且我不能在我的CreateFile方法中结合前4行来减少不重要的代码并使其更具可读性?

public void CreateFile(string path)
{
    Task<byte[]> headerTask = CreateHeaderAsync();
    Task<byte[]> filesTask = CombineFilesAsync();
    byte[] header = headerTask.Result;
    byte[] files = filesTask.Result;

    byte[] combined = new byte[header.Length + files.Length];

    Buffer.BlockCopy(header, 0, combined, 0, header.Length);
    Buffer.BlockCopy(files, 0, combined, header.Length, files.Length);

    Task.Factory.StartNew(() => File.WriteAllBytes(path, combined));
}

private Task<byte[]> CreateHeaderAsync()
{
    return Task.Factory.StartNew(() =>
    {
        StringBuilder sb = new StringBuilder();
        int position = 0;
        foreach (ByteFile file in _files)
        {
            sb.Append(file + "?" + position + Environment.NewLine);
            position += file.Length;
        }
        return Encoding.UTF8.GetBytes(sb + "--header_end--" + Environment.NewLine);
    });
}

private Task<byte[]> CombineFilesAsync()
{
    return Task.Factory.StartNew(() =>
    {
        ByteFile[] arrays = _files.ToArray();

        byte[] rv = new byte[arrays.Sum(a => a.Length)];
        int offset = 0;
        foreach (ByteFile t in arrays)
        {
            var array = Encryption.EncryptBytes(t.Content, "password");

            Buffer.BlockCopy(array, 0, rv, offset, array.Length);
            offset += array.Length;
        }
        return rv;
    });
}

3 个答案:

答案 0 :(得分:2)

  

目标是调用2个方法(CreateHeaderAsync,CombineFilesAsync)并让它们并行工作。

然后尝试Task.WhenAll以异步方式等待完成。

  public async Task CreateFile(string path)
    {
        Task<byte[]> headerTask = CreateHeaderAsync();
        Task<byte[]> filesTask = CombineFilesAsync();

        var allResults = await Task.WhenAll(headerTask, filesTask);
        byte[] header = allResults[0];
        byte[] files = allResults[1];

回复到评论

  

他也不能做byte [] header = await headerTask; byte [] files = await filesTask;他等待一项任务是否重要,即使另一项任务可能在它之前完成?

确定。这是真的。唯一的问题是两个await可以再次恢复呼叫者。所以我更喜欢单一的最终回调。

答案 1 :(得分:0)

如果您希望它们并行运行,您可以使用WhenAll。

https://msdn.microsoft.com/en-us/library/hh194874(v=vs.110).aspx

这样的事情:

public void CreateFile(string path)
{
    Task<byte[]>[] tasks = new [] { CreateHeaderAsync(), CombineFilesAsync() };

    var resultSet = Task.WhenAll(tasks); // create an aggregate task
    try 
    {
        resultSet.Wait(); // await the results
    }
    catch (AggregateException)
    {
        // handle exceptions in the tasks here
    }

    if (resultSet.Status == TaskStatus.RanToCompletion) 
    {
         // get your results
         byte[] header = resultSet.Result[0]; 
         byte[] files = resultSet.Result[1];

         byte[] combined = new byte[header.Length + files.Length];
         Buffer.BlockCopy(header, 0, combined, 0, header.Length);
         Buffer.BlockCopy(files, 0, combined, header.Length, files.Length);

         Task.Factory.StartNew(() => File.WriteAllBytes(path, combined));
    } 
}

此外,在这种情况下,我会发现Task.Run()比Task.Factory.StartNew()更具可读性。

https://blogs.msdn.microsoft.com/pfxteam/2011/10/24/task-run-vs-task-factory-startnew/

它允许您通过将所有任务集中在CreateFile方法中来使CreateHeaderAsync和CombineFilesAsync更小。

public void CreateFile(string path)
{
    Task<byte[]>[] tasks = new [] { 
          Task.Run((byte[])CreateHeaderAsync), 
          Task.Run((byte[])CombineFilesAsync)};

    // rest of the method 
}

private byte[] CombineFilesAsync()
{
     ByteFile[] arrays = _files.ToArray();

     byte[] rv = new byte[arrays.Sum(a => a.Length)];
     int offset = 0;
     foreach (ByteFile t in arrays)
     {
        var array = Encryption.EncryptBytes(t.Content, "password");

        Buffer.BlockCopy(array, 0, rv, offset, array.Length);
        offset += array.Length;
     }
     return rv;
}

写得快一点,没有检查,如果有任何奇怪的错误在那里偷偷道歉:)

答案 2 :(得分:0)

我会选择异步/等待模式。使用Task.Result会导致调用线程被阻塞,这在某些情况下会导致死锁。

如果您不想使用任务创建选项并且您不希望能够取消任务,我建议您使用Task.Run(() => {})创建任务。在大多数情况下,这正是您所需要的。

因此,将您的异步方法签名更改为public async ...,然后调用task.Result而不是等待任务:

var taskResult = await MyMethodAsync();