简体   繁体   中英

Is it safe to mutate DataTable inside an async task in windows forms app that doesn't make use of Task.Run()?

I have 2 examples about mutating the data table inside of an async function. Since the DataTable and DataRow are of reference type, we can pass them into function and mutate - for example remove the row from the data table and this will effect the data table outside of the function. I know that doing so is unsafe in a multi threaded environment.

However this question pertains to the use of async await without making use of Task.Run - so effectively we always have 1 thread - the UI thread running the code.

Example 1-

   private static async Task FuncAsync(DataTable dt, DataRow dr)
    {
        try
        {
            await Task.Delay(2000); //some io bound work here
            
            Thread.Sleep(2000); //some syncronous blocking code here like log to db
            dt.Rows.Remove(dr); //mutate datatable - remove the datarow from the datatable if there is no exception // this can be add/delete or even updating of the row
        }
        catch (Exception e)
        {
            Thread.Sleep(2000); //some syncronous blocking code here like log to db
        }
    }

    private async void Button1_Click(object sender, EventArgs e)
    {
        List<Task> lstTasks = new List<Task>();

        DataTable dt = (DataTable)gridview1.DataSource;

        foreach (DataRow dr in dt.Rows)
        {
            lstTasks.Add(FuncAsync(dt, dr);                
        }            

        while (lstTasks.Any())
        {   
            Task finishedTask = await Task.WhenAny(lstTasks);
            lstTasks.Remove(finishedTask);
            await finishedTask;
            progressbar1.ReportProgress();
        }
        
        MessageBox.Show("Rows that resulted in exception: " + dt.Rows.Count.ToString());
    }
}

Example 2-

private void Form1_Load(object sender, EventArgs e)
        {
            DataTable dt = new DataTable();
            dt.Columns.Add("col1");
            for (int i =0; i < 1000; i++)
            {
                dt.Rows.Add(i);
            }

            dataGridView1.DataSource = dt; //Create datatable with 999 rows
        }

        private async Task FnMutateDtAsync(DataTable dt)
        {
            await Task.Delay(2000); //represents for io work
            
            bool tst = false; //code following this line is syncronous
            foreach (DataRow dr in dt.Rows)
            {
                if (dr["col1"].ToString() == "2") //if 2 exists then delete the 1st row
                {
                    tst = true;
                }
            }

            if (tst)
            {
                dt.Rows.RemoveAt(0); //mutate the datatable - this can be add/delete/modify value of cell
            }
        }

        private async void button1_Click(object sender, EventArgs e)
        {
            List<Task> tsks = new List<Task>();
            for (int i = 0; i < 5; i++) //run 5 tasks async
            {
                tsks.Add(FnMutateDtAsync((DataTable)dataGridView1.DataSource));
            }

            while (tsks.Any())
            {
                Task finishedTask = await Task.WhenAny(tsks);
                tsks.Remove(finishedTask);
                await finishedTask;
                progressbar1.ReportProgress()
            }
        }

In both above examples, I am mutating the datatable from inside the async function.

  1. Is there any harm that I must expect when writing code like this? Can I consider this coding style to be thread safe since at any point only 1 thread is doing all the work?

  2. Is it safe to do ConfigureAwait(false) on the await line?

In short: All the code is executed synchronously.


The UI-thread contains a WindowsFormsSynchronizationContext in it's SynchronizationContext, it will be used to post the remaining code after awaits of the 'async-statemachine' to the UI thread.

The problem you will be facing is that the Thread.Sleep(2000); is executed on the UI-thread, so it will block the UI-thread. Mostly db related actions do support async calling.

Another problem you might be facing is, when the dt.Rows.Remove(dr); is called before any await (so it is executed directly) you'll get a Collection was modified on the foreach (DataRow dr in dt.Rows)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM