C# 通过嵌套方法使用TPL时无法更新UI元素
我正在尝试使用TPL从多个方法更新UI元素。当执行第一个方法时,元素会得到更新,但是当调用第一个方法中嵌套的子方法时,我会得到一个C# 通过嵌套方法使用TPL时无法更新UI元素,c#,asynchronous,progress-bar,task-parallel-library,C#,Asynchronous,Progress Bar,Task Parallel Library,我正在尝试使用TPL从多个方法更新UI元素。当执行第一个方法时,元素会得到更新,但是当调用第一个方法中嵌套的子方法时,我会得到一个invalidoOperationException-当前SynchronizationContext不能用作TaskScheduler。我甚至尝试将代码转换为async-wait模式,但没有成功 编辑1:无法复制InvalidOperationException。我现在收到此错误-当前SynchronizationContext不能用作TaskScheduler 编
invalidoOperationException
-当前SynchronizationContext不能用作TaskScheduler。我甚至尝试将代码转换为async
-wait
模式,但没有成功
编辑1:无法复制InvalidOperationException。我现在收到此错误-当前SynchronizationContext不能用作TaskScheduler
编辑2:复制了InvalidOperationException。这是由于curren SynchronizationContext不能用作TaskScheduler而导致的。堆栈跟踪如下所示:
在
System.Threading.Tasks.SynchronizationContextTaskScheduler..ctor()在 System.Threading.Tasks.TaskScheduler.FromCurrentSynchronizationContext() 在
在
位于System.Threading.Tasks.Task.InnerInvoke()的 System.Threading.Tasks.Task.Execute() 另外,我注意到,如果在按钮单击事件中调用UpdateValuesInDb方法,更新将传递到UI 下面是我的代码以及我所做的研究,这些研究在代码的注释中
private void btnUploadToDb_Click(object sender, EventArgs e)
{
UploadToDb();
}
private static void UploadToDb()
{
Task.Factory.StartNew(()=>
{
for (int i = 0; i <= maxRecords - 1; i++)
{
// Code for inserting into Db.
// Update progress to progressbar and label on UI form.
// Borrowed from Stephen Cleary's article
// http://blog.stephencleary.com/2010/06/reporting-progress-from-tasks.html
progressReporter.ReportProgress(() => {
// Can this be done in a better way than enclosing in a
// ReportProgress before entering the for loop?
progressbar.maximum=maxRecords;
label.text="Uploading " + i;
progressbar.value=i;
});
}
progressReporter.ReportProgress(() => {
label.Text = "Updating values in DB, please wait...";
});
// When this call is made the subsequent updates to the main form
// were not successful due to 'InvalidOperationException'.
UpdateValuesInDb();
});
}
private void UpdateValuesInDb();
{
// Code for updating the values.
// The below method should be called in sequence and labels updated in the same sequence.
// Report progress to UI for values of type 1.
// label.text="Moving values of type 1..."; // This has not been implemented because of cross thread exceptions
// This is what I'd like to achieve.
MoveValuesToNewDbDeleteFromSourceDb(values); // This call has to complete first
// Report progress to UI for values of type 2.
// label.text="Moving values of type 2..."; // This has not been implemented because of cross thread exceptions
// This is what I'd like to achieve.
MoveValuesToNewDbDeleteFromSourceDb(values); // This call has to complete second.
// Report progress to UI for values of type 3.
label.text="Moving values of type 3..."; // This has not been implemented because of cross thread exceptions
// This is what I'd like to achieve.
MoveValuesToNewDbDeleteFromSourceDb(values); // This call has to complete third.
// Report progress to UI for values of type 4.
// label.text="Moving values of type 4..."; // This has not been implemented because of cross thread exceptions
// This is what I'd like to achieve.
MoveValuesToNewDbDeleteFromSourceDb(values); // This call is the final.
}
private void MoveValuesToNewDbDeleteFromSourceDb(string values)
{
var progressReporter = new ProgressReporter();
Task.Factory.StartNew(() => {
for (int i = 0; i <= dt.Rows.Count - 1; i++)
{
// Tried using Stephen Cleary's code here but it fails with
progressReporter.ReportProgress(() =>
{
progressBar.Maximum = maxRecords - 1;
Label.Text = "Uploading " + i;
progressBar.Value = i;
});
// Need to update label and progress in for loop as mentioned above.
}
});
}
private void btnuploaddtodb\u单击(对象发送方,事件参数e)
{
上传todb();
}
私有静态void UploadToDb()
{
Task.Factory.StartNew(()=>
{
对于(int i=0;i
{
progressBar.Maximum=maxRecords-1;
Label.Text=“上传”+i;
progressBar.Value=i;
});
//如上所述,需要更新for循环中的标签和进度。
}
});
}
我甚至尝试将现有代码转换为使用async await方法,但在编译过程中失败,并声明它是不可等待的。
这就是我试图改变的:
private async void btnUploadToDb_Click(object sender, EventArgs e)
{
await UploadToDb(); // Got the error Type System.Threading.Tasks.Task is not awaitable.
}
private async Task UploadToDb()
{
// Codes is same as the previous one, I've just used the async
// modifier, though I do not know what to await here.
// Tried to assign await as follows:
await Task.Factory.StartNew(() => {
for (int i = 0; i <= dt.Rows.Count - 1; i++)
{
// Need to update label and progress bar here.
}
});
// The above results in a compile error:
// 'Type System.Threading.Tasks.Task is not awaitable'.
}
private async void btnuploadtob\u单击(对象发送方,事件参数e)
{
wait UploadToDb();//获取错误类型System.Threading.Tasks.Task不可等待。
}
专用异步任务上载到数据库()
{
//代码与前面的代码相同,我刚刚使用了异步
//修饰符,虽然我不知道在这里等待什么。
//尝试按如下方式分配等待:
等待任务。工厂。开始新建(()=>{
对于(int i=0;i您不想在MoveValuesToNewDbDeleteFromSourceDb
方法中启动新任务,因为:
应按顺序调用以下方法,并按相同顺序更新标签
为什么不把这个方法改成这样:
private void MoveValuesToNewDbDeleteFromSourceDb(string valuesType, string values)
{
progressReporter.ReportProgress(() => {
label.text = string.Format("Moving values of type {0}...", valuesType);
});
for (int i = 0; i <= dt.Rows.Count - 1; i++)
{
// Do your move values thing.
// Report progress
progressReporter.ReportProgress(() => {
// ...
});
}
}
注:如博客中所述,ProgressReporter
已“弃用”:
更新,2012-02-16:此帖子中的信息是旧的。有关更好的解决方案,请参阅新帖子
第二个注意事项:我假设您的progressReporter
实例是一个成员变量,它是在您的UI线程上创建的。如果不是这样,这就解释了您遇到问题的原因:将其设为成员变量,在创建表单时构造它。
最后,您可以根据需要使用一个简单得多的实现
public class ProgressReporter
{
private readonly SynchronizationContext _syncContext = SynchronizationContext.Current;
public void ReportProgress(Action progressAction)
{
_syncContext.Post(new SendOrPostCallback(unused => progressAction()), null);
}
}
您不想在MoveValuesToNewDbDeleteFromSourceDb
方法中启动新任务,因为:
应按顺序调用以下方法,并按相同顺序更新标签
为什么不把这个方法改成这样:
private void MoveValuesToNewDbDeleteFromSourceDb(string valuesType, string values)
{
progressReporter.ReportProgress(() => {
label.text = string.Format("Moving values of type {0}...", valuesType);
});
for (int i = 0; i <= dt.Rows.Count - 1; i++)
{
// Do your move values thing.
// Report progress
progressReporter.ReportProgress(() => {
// ...
});
}
}
注:如博客中所述,ProgressReporter
已“弃用”:
更新,2012-02-16:此帖子中的信息是旧的。有关更好的解决方案,请参阅新帖子
第二个注意事项:我假设您的progressReporter
实例是一个成员变量,它是在您的UI线程上创建的。如果不是这样,这就解释了您遇到问题的原因:将其设为成员变量,在创建表单时构造它。
最后,您可以根据需要使用一个简单得多的实现
public class ProgressReporter
{
private readonly SynchronizationContext _syncContext = SynchronizationContext.Current;
public void ReportProgress(Action progressAction)
{
_syncContext.Post(new SendOrPostCallback(unused => progressAction()), null);
}
}
您不想在MoveValuesToNewDbDeleteFromSourceDb
方法中启动新任务,因为:
应按顺序调用以下方法,并按相同顺序更新标签
为什么不把这个方法改成这样:
private void MoveValuesToNewDbDeleteFromSourceDb(string valuesType, string values)
{
progressReporter.ReportProgress(() => {
label.text = string.Format("Moving values of type {0}...", valuesType);
});
for (int i = 0; i <= dt.Rows.Count - 1; i++)
{
// Do your move values thing.
// Report progress
progressReporter.ReportProgress(() => {
// ...
});
}
}
注:如博客中所述,ProgressReporter
已“弃用”:
更新,2012-02-16:此帖子中的信息是旧的。有关更好的解决方案,请参阅新帖子
第二个注意事项:我假设您的progressReporter
实例是一个成员变量,它是在您的UI线程上创建的。如果不是这样,这就解释了您遇到问题的原因:将其设为成员变量,在创建表单时构造它。
最后,您可以根据需要使用一个简单得多的实现
public class ProgressReporter
{
private readonly SynchronizationContext _syncContext = SynchronizationContext.Current;
public void ReportProgress(Action progressAction)
{
_syncContext.Post(new SendOrPostCallback(unused => progressAction()), null);
}
}
您不想在MoveValuesToNewDbDeleteFromSourceDb
方法中启动新任务,因为:
应按顺序调用以下方法,并按相同顺序更新标签
为什么不把这个方法改成这样:
private void MoveValuesToNewDbDeleteFromSourceDb(string valuesType, string values)
{
progressReporter.ReportProgress(() => {
label.text = string.Format("Moving values of type {0}...", valuesType);
});
for (int i = 0; i <= dt.Rows.Count - 1; i++)
{
// Do your move values thing.
// Report progress
progressReporter.ReportProgress(() => {
// ...
});
}
}
注:如博客中所述,ProgressReporter
已“弃用”:
更新,2012-02-16:此帖子中的信息是旧的。有关更好的解决方案,请参阅新帖子
第二个注意事项:我假设您的progressReporter
实例是一个成员变量,它是在您的UI线程上创建的。如果不是这样,这就解释了您遇到问题的原因:将其设为成员变量,在创建表单时构造它。
最后,您可以根据需要使用一个简单得多的实现
public class ProgressReporter
{
private readonly SynchronizationContext _syncContext = SynchronizationContext.Current;
public void ReportProgress(Action progressAction)
{
_syncContext.Post(new SendOrPostCallback(unused => progressAction()), null);
}
}
如果你的申请