如何限制.Net中的异步函数?

如何限制.Net中的异步函数?,.net,async-await,throttling,.net,Async Await,Throttling,我在.Net中使用异步等待。如何限制并发异步调用的数量?注意:我将此保留在此处,以备将来使用。不要这样做,因为当同时执行任何任务时,上会有太多任务等待执行。这堆东西会越来越深 根据Stephen Toub的代码: const int CONCURRENCY_LEVEL = 15; Uri [] urls = …; int nextIndex = 0; var imageTasks = new List<Task<Bitmap>>(); while(nextIndex &l

我在.Net中使用异步等待。如何限制并发异步调用的数量?

注意:我将此保留在此处,以备将来使用。不要这样做,因为当同时执行任何任务时,
上会有太多任务等待执行。这堆东西会越来越深

根据Stephen Toub的代码:

const int CONCURRENCY_LEVEL = 15;
Uri [] urls = …;
int nextIndex = 0;
var imageTasks = new List<Task<Bitmap>>();
while(nextIndex < CONCURRENCY_LEVEL && nextIndex < urls.Length)
{
    imageTasks.Add(GetBitmapAsync(urls[nextIndex]));
    nextIndex++;
}

while(imageTasks.Count > 0)
{
    try
    {
        Task<Bitmap> imageTask = await Task.WhenAny(imageTasks);
        imageTasks.Remove(imageTask);

        Bitmap image = await imageTask;
        panel.AddImage(image);
    }
    catch(Exception exc) { Log(exc); }

    if (nextIndex < urls.Length)
    {
        imageTasks.Add(GetBitmapAsync(urls[nextIndex]));
        nextIndex++;
    }
}
与:

Dim f As Func(属于任务(属于整数))
f=函数()
返回示例异步(param1,param2)
端函数
Const并发_任务为整数=4
返回ThrottleAsync(f,“exampleAync”,并发任务)
请注意,我们必须将对任务的调用包装在函数
f
中,否则我们就已经开始了任务。ThrottleAsync的第二个参数是标识“组”的任何对象;我用了一根绳子。同一“组”中的所有异步任务仅限于
并发任务
任务,在本例中为4

下面的示例代码显示了如何一次只运行四个线程<代码>一切就绪立即显示,因为子例程是异步的。此外,即使线程的开始或结束顺序不正确,“输出”行仍将与输入的顺序相同

Dim结果作为新列表(任务(整数))
对于i,整数=0到20
尺寸j为整数=i
Dim f作为Func(任务的(整数的))
f=函数()作为任务(整数的)
返回Task.Run(函数()为整数
Debug.WriteLine(DateTime.Now&“Starting”&j)
系统线程线程睡眠(5000)
Debug.WriteLine(DateTime.Now&“Ending”&j)
返回j
终端功能)
端函数
Const CONCURRENT_上载为整数=4
结果.添加(ThrottleAsync(f,“PutOjbectAsync”,并发上传))
下一个
Debug.WriteLine(“准备就绪!”)
对于结果中的每个x As任务(整数)
Debug.WriteLine(DateTime.Now&“Output:”&wait x)
下一个

一个相对简单的方法是(ab)使用TPL数据流。比如:

public IEnumerable<TOutput> AsyncThrottle<TInput, TOutput>(
    IEnumerable<TInput> inputs, Func<TInput, Task<TOutput>> asyncFunction,
    int maxDegreeOfParallelism)
{
    var outputs = new ConcurrentQueue<TOutput>();

    var block = new ActionBlock<TInput>(
        async x => outputs.Enqueue(await asyncFunction(x)),
        new ExecutionDataflowBlockOptions
        { MaxDgreeOfParallelism = maxDegreeOfParallelism });

    foreach (var input in inputs)
        block.Send(input);

    block.Complete();
    block.Completion.Wait();

    return outputs.ToArray();
}
public IEnumerable异步(
IEnumerable输入,Func异步函数,
int maxDegreeOfParallelism)
{
var输出=新的ConcurrentQueue();
var block=新动作块(
异步x=>outputs.Enqueue(等待异步函数(x)),
新的ExecutionDataflowBlockOptions
{MaxDgreeOfParallelism=maxDegreeOfParallelism});
foreach(输入中的var输入)
块。发送(输入);
block.Complete();
block.Completion.Wait();
返回输出。ToArray();
}

根据代码的不同,最简单的方法可能是使用Parallel.For(每个)并在Parallel选项中指定最大并行度

我更喜欢这种技术。我正在使用
TaskCompletionSource
为传入任务创建输出任务。这是必需的,因为我想在运行任务之前返回任务!下面的类将每个输入
Func(of Task(of Object))
与一个
TaskCompletionSource
相关联,后者立即返回并将它们放入队列中

队列中的元素被排到一个正在运行的任务列表中,一个延续设置
TaskCompletionSource
。当循环中有任何
时,对
的调用确保在释放空间时将元素从队列移动到运行列表。还有一个检查,以确保每次出现一个
时不会有多个
,尽管它可能存在并发性问题

要使用,只需替换如下同步函数:

Task.Run(AddressOf MySyncFunction)”可能有很多
为此:

Dim t1作为新节流阀(4)
t1.Run(AddressOf MySyncFunction)'其中很多,但一次只运行4个。
对于已经返回任务的函数,重要的是将它们转换为返回任务的函数,以便Thotler可以运行它们。替换:

NewTask=myfunctionsync()
与:

NewTask=t1.Run(函数()返回myfunctionsync())
下面的类还为Throttler.Run()实现了许多不同的签名,具体取决于函数是否为sync/async、has/has not input、has/has not output。将任务转换为任务(输出)尤其棘手

类节流器
属性MaxCount为整数
Sub New(可选的最大计数为整数=1)
Me.MaxCount=MaxCount
端接头
作为新任务运行的专用列表(任务)
私有等待作为新的Concurrent.ConcurrentQueue(属于System.Tuple(属于Func(属于Task(属于Object)),TaskCompletionSource(属于Object)))
私有AlreadyWaiting为布尔值
异步子生成器()
如果已准备等待,则退出Sub
AlreadyWaiting=True
等待时执行此操作。计数>0
Dim CurrentWait As System.Tuple(属于Func(属于任务(属于对象)),TaskCompletionSource(属于对象))=无
运行时执行。Count0
将服务员作为任务(任务的)
服务员=任务。何时(运行)
Dim FinishedTask As Task=等待服务员
等待完成任务
正在运行。删除(完成任务)
如果结束
环
AlreadyWaiting=False
端接头
函数运行(f作为Func(任务的(对象的)))作为任务(