[英]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 个线程在做所有的工作吗?
在等待线上执行 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.