带有静态变量的异步函数?

时间:2018-07-09 13:22:11

标签: c# static async-await

抱歉,我现在不在办公室,所以我没有确切的代码,但是希望在以下情况下提供一些建议。

情况

我有一个带有一个按钮的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!

1 个答案:

答案 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。因此,我们现在知道已经有一个任务在运行,可以终止由返回完成的第二个任务。

我还建议您通过对用户的视觉反馈来显示此行为。例如,只要操作按照上面的注释中的建议运行,就禁用该按钮。