C#在同步上下文中运行异步任务 [英] C# running async tasks in sync context

查看:132
本文介绍了C#在同步上下文中运行异步任务的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在C#异步方面没有太多经验.

I don't have much experience in C# async.

任务-从Internet加载位图.以前,我只是按1个1个同步地加载它们.异步加载它们可以更快地得到结果.在下面,我举了两个例子,我如何获得单个图像-GetImageGetImageAsync.对于图像列表,我将使用LoadImagesLoadImages2.

Task - load bitmaps from the internet. Before, I was just loading them 1 by 1, in sync. Loading them in async would give results quicker. Down below, I made two examples, how I could get single image - GetImage and GetImageAsync. And for a list of Images, I would use LoadImages and LoadImages2.

LoadImages将在异步中运行同步功能(全部同时(?)),LoadImages2将在异步中运行异步功能并产生相同的结果(?). 我不完全了解的东西-在GetImageAsync await request.GetResponseAsync()中.我真的需要吗?这是做同一件事的一种更好"的方式吗? LoadImagesLoadImages2之间真的有任何区别吗?

LoadImages would run sync functions in async (all at the same time (?)), LoadImages2 would run async functions in async and produce the same result (?). The thing, that I don't fully understand - in GetImageAsync await request.GetResponseAsync(). Do I really need it? Is it a "better" way of doing the same thing? Are there really any difference between LoadImages and LoadImages2.

目前,我正在考虑选择GetImageLoadImages选项.另外,我不想用async Task装饰每个功能,我只需要异步加载这些图像即可.

At the moment, I am thinking of choosing GetImage and LoadImages option. Also, I don't want to decorate every function with async Task, I only need to load these images in async.

public Bitmap GetImage(string url)
{
    HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest;
    using (WebResponse response = request.GetResponse())
    using (Stream responseStream = response.GetResponseStream())
        return new Bitmap(responseStream);
}

public async Task<Bitmap> GetImageAsync(string url)
{
    HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest;
    using (WebResponse response = await request.GetResponseAsync())
    using (Stream responseStream = response.GetResponseStream())
        return new Bitmap(responseStream);
}

private Dictionary<string, Bitmap> LoadImages(List<string> urls)
{
    Dictionary<string, Bitmap> images = new Dictionary<string, Bitmap>();
    Task.WaitAll(urls.Select(url => 
        Task.Run(() =>
        {
            images.Add(url, GetImage(url));
        })).ToArray());
    return images;
}

private Dictionary<string, Bitmap> LoadImages2(List<string> urls)
{
    Dictionary<string, Bitmap> images = new Dictionary<string, Bitmap>();
    Task.WhenAll(urls.Select(url =>
        Task.Run(async () =>
        {
            images.Add(url, await GetImageAsync(url));
        })));
    return images;
}

推荐答案

此处的术语和技术选择有些混乱.

There's some confusion around terminology and technology choices here.

之前,我只是按1个1个同步地加载它们.异步加载它们可以更快地得到结果.

Before, I was just loading them 1 by 1, in sync. Loading them in async would give results quicker.

您的意思是 serial concurrent ,而不是 sync async .串行是一次一次,而并发是一次多个事情.同步代码可以是串行或并发的,异步代码可以是串行或并发的.

What you mean is serial versus concurrent, not sync versus async. Serial is one-at-a-time, and concurrent is multiple things at once. Synchronous code can be serial or concurrent, and asynchronous code can be serial or concurrent.

第二,并发并行性. Task.Run是并行性的一种形式,它是通过向问题添加线程来实现并发的一种方式. Asynchrony 是一种通过释放 线程来实现并发的方法.

Secondly, concurrency versus parallelism. Task.Run is a form of parallelism, which is a way to achieve concurrency by adding threads to the problem. Asynchrony is a way to achieve concurrency by freeing up threads.

LoadImages是将并行性与同步代码一起使用的示例.这种方法的优点是它使顶级方法保持同步,因此无需更改任何调用代码.缺点是,这在资源使用方面很浪费,并且与下面的情况没有很好的概念契合(I/O绑定代码更自然地由异步API表示).

LoadImages is an example of using parallelism with synchronous code. The advantage of this approach is it keeps the top-level method synchronous, so none of the calling code has to change. The disadvantage is that it's wasteful in terms of resource use, and not a good conceptual fit for what's going on underneath (I/O-bound code is more naturally represented by asynchronous APIs).

LoadImages2是并行和异步代码的混合,这有点令人困惑.没有线程(即Task.Run)更容易表示异步并发.返回值而不是将集合更新作为副作用也更自然.所以,像这样:

LoadImages2 is a mixture of parallel and asynchronous code which is a bit confusing. Asynchronous concurrency is more easily represented without threads (i.e., Task.Run). It's also more natural to return values rather than update collections as side effects. So, something like this:

private async Task<Dictionary<string, Bitmap>> LoadImagesAsync(List<string> urls)
{
  Bitmap[] result = await Task.WhenAll(urls.Select(url => GetImageAsync(url)));

  return Enumerable.Range(0, urls.Length).ToDictionary(i => urls[i], i => result[i]);
}

P.S.如果您决定使用(同步)LoadImages,则需要修复一个竞争条件,在该条件下,各个并行线程都将尝试在不锁定字典的情况下更新字典.

P.S. If you do decide to go with (the synchronous) LoadImages, you'll need to fix a race condition where the various parallel threads will all try to update the dictionary without locking it.

这篇关于C#在同步上下文中运行异步任务的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆