抱歉,我现在不在办公室,所以我没有确切的代码,但是希望在以下情况下提供一些建议。
情况
我有一个带有一个按钮的winform应用程序。 (实际上是几个,但可以减少为一个)。 该按钮调用系列异步操作。 每次调用此按钮时,它所调用的操作集都会分配一个唯一的序列号。 (此操作涉及一些HttpClient操作)
如果在完成第一组操作之前再次按下按钮,则可能会返回错误。
我有什么
static int serialNumber=0;
private async void button_Clicked(object sender, EventArgs e)
{ int old= serialNumber;
serialNumber++;
//Do some stuff
//here and then
var result= await callFirstOperation(serialNumber);
if(!result)
{ serialNumber=old;
return;
}
//Do some more stuff
var result2= await callSecondOperation(serialNumber);
if(!result2)
{ serialNumber=old;
return;
}
//Some more stuff and async operations ....
Trace.WriteLine("Fiuuu... finished!");
}
如您所见,按下按钮时,其余的异步异步操作将使用串行操作,但是如果其中之一失败,则数字将恢复为旧值。
如果我按下按钮并等待所有操作完成,这将不是问题,但是如果我第二次在完成第一组操作之前第二次按下按钮,会发生什么情况。
我的东西序列号将受到(第二次呼叫的影响),这也将影响第一组的以下操作。
实现目标的正确方法是什么?
可以通过异步函数安全地使用静态变量吗?
编辑: 我认为不重置序列号会有所帮助,但请考虑一下
第一次按钮->序列号:1
(第一次)操作1(1)
(第一次)操作2(1)
然后单击第二次按钮->序列号:2
(第二次)操作1(2)返回错误
(第一次)操作3(2)<---等待!这应该是1而不是2!
答案 0 :(得分:1)
据我所知,您的问题是您希望阻止程序同时运行两次异步操作。
为此,您可以使用信号量。 这种线程安全的结构基本上像一个计数器。每次线程进入都会倒数。达到0后,将不再有线程可以进入,而将不得不等待。
Semaphore semaphore = new Semaphore(1, 1);
此信号量将允许1个线程进入,并将初始化为1。 要“输入”您调用的受信号量保护的代码块:
semaphore.WaitOne()
这会将计数器递减1,现在可以阻止任何尝试调用WaitOne
的任务从此调用中返回。
此后,您可以确定一次仅一次完成一项任务。
要打开您调用的下一个任务的信号灯:
semaphore.Release();
为了更好地理解,我整理了一个与您的问题有关的最小示例:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
Semaphore semaphore = new Semaphore(1, 1);
private async void button1_Click(object sender, EventArgs e)
{
if (!semaphore.WaitOne(0))
{
MessageBox.Show("Long running task hasn't finished yet!");
return;
}
await LongRunningTask();
semaphore.Release();
}
public async Task LongRunningTask()
{
await Task.Delay(5000);
MessageBox.Show("Long running finished...");
}
}
这里特别是WaitOne
的呼叫。第一个参数(0)指定每个要输入但由于某个任务已在运行而不能输入的任务。返回前将等待0毫秒。这里的技巧是,如果拒绝,则条目WaitOne
将返回false
。因此,我们现在知道已经有一个任务在运行,可以终止由返回完成的第二个任务。
我还建议您通过对用户的视觉反馈来显示此行为。例如,只要操作按照上面的注释中的建议运行,就禁用该按钮。