簡體   English   中英

在不使用 Task.Run() 的 Windows 窗體應用程序中的異步任務中改變 DataTable 是否安全?

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

我有 2 個關於在異步函數中改變數據表的示例。 由於 DataTable 和 DataRow 是引用類型,我們可以將它們傳遞給函數並進行變異 - 例如從數據表中刪除行,這將影響函數外部的數據表。 我知道在多線程環境中這樣做是不安全的。

然而,這個問題與不使用 Task.Run 的 async await 的使用有關 - 因此有效地我們總是有 1 個線程 - 運行代碼的 UI 線程。

示例 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());
    }
}

示例 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()
            }
        }

在上面的兩個例子中,我從異步函數內部改變了數據表。

  1. 在編寫這樣的代碼時,我必須預料到有什么危害嗎? 我可以認為這種編碼風格是線程安全的,因為在任何時候都只有 1 個線程在做所有的工作嗎?

  2. 在等待線上執行 ConfigureAwait(false) 是否安全?

簡而言之:所有代碼都是同步執行的。


UI 線程在它的 SynchronizationContext 中包含一個WindowsFormsSynchronizationContext ,它將用於在等待“async-statemachine”到 UI 線程后發布剩余的代碼。

您將面臨的問題是Thread.Sleep(2000); 在 UI 線程上執行,因此它會阻塞 UI 線程。 大多數與數據庫相關的操作都支持異步調用。

您可能面臨的另一個問題是,當dt.Rows.Remove(dr); 在任何等待之前調用(因此它直接執行)您將在foreach (DataRow dr in dt.Rows)上修改一個集合foreach (DataRow dr in dt.Rows)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM