Home > Back-end >  Execution of tasks launched asynchronously by button sequentially
Execution of tasks launched asynchronously by button sequentially

Time:08-14

It is necessary to execute the methods sequentially in the order they were started, but without stopping the UI. In the example that I made, the operations are performed asynchronously, which leads to incorrect entries in the ListNumber list.

 public Form1()
    {
        InitializeComponent();
        ListNumber = new List<string>();
    }
    List<string> ListNumber { get; set; }
    private async void button1_Click(object sender, EventArgs e)
    {
        textBox1.Text = await Task.Run(() => MessageAsync());
    }
    private async Task<string> MessageAsync()
    {
        var concat = "";
        await NumberAsync();
        foreach (string number in ListNumber)
        {
            concat  = number   ", ";
        }
        return concat;
    }

    private async Task NumberAsync()
    {
        for(int i = 0; i < 30; i  )
        {
            ListNumber.Add(i.ToString());
            await Task.Delay(300);
        }
        
    }

If you quickly click on the button, the calling method gives the following result: the result of the program

CodePudding user response:

Xerillio's proposed solution does work as long as you don't expect the button to be responsive after be pressed:

private async void button1_Click(object sender, EventArgs e)
{
    button1.IsEnabled = false;
    textBox1.Text = await Task.Run(() => MessageAsync());
    button1.IsEnabled = true;
}

If you need to be able to use the button while your task is running, or in other words you expect several things to need access to the ListNumber resource you need to design a system for controlling access to that resource. Only allowing one producer to add values to the list at a time for instance would be a method but it all depends on what behavior you want to see.

Below is a working version which controls access to the LisNumber object using a semaphore.

    public MainWindow()
    {         
        InitializeComponent();
        ListNumber = new List<string>();
        semaphore = new SemaphoreSlim(1, 1);
    }
    SemaphoreSlim semaphore;
    List<string> ListNumber { get; set; }

    private async void button1_Click(object sender, EventArgs e)
    {
        await NumberAsync();
        textBox1.Text = await Message();
    }

    private async Task<string> Message()
    {
        await semaphore.WaitAsync();
        var concat = "";
        foreach (string number in ListNumber)
        {
            concat  = number   ", ";
        }
        semaphore.Release();
        return concat;
    }

    private async Task NumberAsync()
    {
        await semaphore.WaitAsync();
        for (int i = 0; i < 30; i  )
        {
            ListNumber.Add(i.ToString());
            await Task.Delay(300);
        }
        semaphore.Release();
    }

You could also just wrap the button call in the semaphore if you wanted:

private async void button1_Click(object sender, EventArgs e)
{
    await semaphore.WaitAsync();
    await NumberAsync();
    textBox1.Text = await Message();
    semaphore.Release();
}
  • Related