Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/309.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何最好地防止在异步方法完成之前再次运行它?_C#_Asynchronous_Async Await - Fatal编程技术网

C# 如何最好地防止在异步方法完成之前再次运行它?

C# 如何最好地防止在异步方法完成之前再次运行它?,c#,asynchronous,async-await,C#,Asynchronous,Async Await,我使用这种模式来防止在异步方法有机会完成之前调用它 我的解决方案涉及需要一个标志,然后需要在标志周围锁定,感觉非常冗长。有没有更自然的方法来实现这一点 public class MyClass { private object SyncIsFooRunning = new object(); private bool IsFooRunning { get; set;} public async Task FooAsync() { try

我使用这种模式来防止在异步方法有机会完成之前调用它

我的解决方案涉及需要一个标志,然后需要在标志周围锁定,感觉非常冗长。有没有更自然的方法来实现这一点

public class MyClass
{
    private object SyncIsFooRunning = new object();
    private bool IsFooRunning { get; set;}

    public async Task FooAsync()
    {
        try
        {
            lock(SyncIsFooRunning)
            {
                if(IsFooRunning)
                    return;

                IsFooRunning = true;
            }

            // Use a semaphore to enforce maximum number of Tasks which are able to run concurrently.
            var semaphoreSlim = new SemaphoreSlim(5);
            var trackedTasks = new List<Task>();

            for(int i = 0; i < 100; i++)
            {
                await semaphoreSlim.WaitAsync();

                trackedTasks.Add(Task.Run(() =>
                {
                    // DoTask();
                    semaphoreSlim.Release();
                }));
            }

            // Using await makes try/catch/finally possible.
            await Task.WhenAll(trackedTasks);
        }
        finally
        {
            lock(SyncIsFooRunning)
            {
                IsFooRunning = false;
            }
        }
    }
}
公共类MyClass
{
私有对象SyncIsFooRunning=新对象();
private bool isfoo正在运行{get;set;}
公共异步任务fooancy()
{
尝试
{
锁定(SyncIsFooRunning)
{
如果(正在运行)
返回;
IsFooRunning=true;
}
//使用信号量强制执行能够并发运行的最大任务数。
var-semaphoreSlim=新的信号量lim(5);
var trackedTasks=新列表();
对于(int i=0;i<100;i++)
{
等待信号量lim.WaitAsync();
trackedTasks.Add(Task.Run)(()=>
{
//DoTask();
semaphoreSlim.Release();
}));
}
//使用wait使try/catch/finally成为可能。
等待任务。WhenAll(跟踪任务);
}
最后
{
锁定(SyncIsFooRunning)
{
IsFooRunning=false;
}
}
}
}

如注释中所述,如果您愿意,可以使用
Interlocked.CompareExchange()

public class MyClass
{
    private int _flag;

    public async Task FooAsync()
    {
        try
        {
            if (Interlocked.CompareExchange(ref _flag, 1, 0) == 1)
            {
                return;
            }

            // do stuff
        }
        finally
        {
            Interlocked.Exchange(ref _flag, 0);
        }
    }
}
也就是说,我认为这太过分了。在这种情况下使用
lock
没有什么错,特别是如果您不希望在方法上有太多争用的话。我认为更好的做法是包装该方法,这样无论是否启动了新的异步操作,调用方都可以等待结果:

public class MyClass
{
    private readonly object _lock = new object();
    private Task _task;

    public Task FooAsync()
    {
            lock (_lock)
            {
                return _task != null ? _task : (_task = FooAsyncImpl());
            }
    }

    public async Task FooAsyncImpl()
    {
        try
        {
            // do async stuff
        }
        finally
        {
            lock (_lock) _task = null;
        }
    }
}
最后,在评论中,您这样说:

似乎有点奇怪,所有的返回类型仍然对任务有效


我不明白你的意思。在您的方法中,唯一有效的返回类型是
void
Task
。如果您的
return
语句返回了实际值,则必须使用
Task
,其中
T
是返回语句返回的类型。

而不是
lock()
,您可以使用
interlocted.Exchange()
。不要使用
async void
@Slaks-interlocted.Exchange不支持bools。我将代码更改为async任务,但它不需要任何其他更改。所有返回类型仍然对任务有效似乎有点奇怪?您可以简化
return\u Task!=空的_任务:(_task=fooancimpl())
返回任务??(_task=fooancimpl())