从 DoWork 处理程序调用方法?
Calling method from DoWork handler?
我正在尝试使用 BackgroundWorker
来完成任务。我已经让工作人员正确地 运行,在 DoWork
方法下它然后调用另一个执行的方法但后来我遇到了我的问题:当该方法试图调用另一个方法时它没有成功并且没有抛出一个异常,我只能认为这是我在 BackgroundWorker
上做错的事情,因为当 运行 在 UI 线程上测试方法按预期执行时。
这是我 运行 我的工人所在的地方:
private void btnAddShots_Click(object sender, EventArgs e)
{
backgroundWorker.RunWorkerAsync();
}
这是我的 DoWork
方法:
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
int noOfShots = dataGridShots.Rows.Count - 1;
int count = 0;
while (count < noOfShots)
{
addTaskPair(dataGridShots.Rows[count].Cells[0].Value.ToString(),
dataGridShots.Rows[count].Cells[1].Value.ToString(),
dataGridShots.Rows[count].Cells[2].Value.ToString());
count += 1;
}
}
这是我的工作人员调用的 addTaskPair
方法的精简版本:
private void addTaskPair(string taskName, string taskDescription, string taskPriority)
{
try
{
Task trackingTask = new Task();
trackingTask.content = taskName;
trackingTask.description = taskDescription;
trackingTask.priority = taskPriority;
string trackingJson = JsonConvert.SerializeObject(trackingTask);
trackingJson = "{ \"todo-item\":" + trackingJson + " }";
string jsonResponse;
jsonResponse = postJSON(trackingJson, teamworkURL + "/tasklists/"
+ todoLists.todoLists[cmbTrackingList.SelectedIndex].id + "/tasks.json");
}
catch (Exception e)
{
debugMessage(e.ToString());
}
}
在上面的示例中,您会看到我调用了方法 postJSON
,这是我遇到困难的地方。通过测试我已经验证了上面的方法运行s,但是postJSON
方法在这个线程中被调用时根本没有运行。
我在研究这个问题时看到了很多对 Invoking
的引用,但它们似乎都适用于更改 ui 控件,而我不需要这样做(尽管进度条是使用ProgressChanged
BackgroundWorker
事件)。
如果需要,我可以进一步澄清我的问题,但我真的希望得到这方面的帮助,因为我以前从未成功地使用过 backgroundworker 或线程(我不是专业人士,我相信你可以从我的代码)。
您正在使用来自 BackgroundWorker.DoWork
事件处理程序的 UI 控件。不要那样做。
在启动 BackgroundWorker
之前收集数据,并将其作为参数传递给 RunWorkerAsync
方法。不要触摸 BackgroundWorker.DoWork
中的 UI - 通过 ReportProgress
方法更新进度是可以的。
此外,如果您 运行 使用 .NET 4.5+,您可能需要考虑改用新的 Task
模式。它仍然需要您事先收集数据进行处理,但它更容易使用:
(EDIT:正如 Peter 所建议的,无效访问发生在 cmbTrackingList.SelectedIndex
中;我已将其包含在下面的代码中。这正是我建议的原因对在单独线程中发生的操作使用 static
方法 - 这会让您对正在处理的数据有更多的思考)
var todoList = todoLists.todoLists[cmbTrackingList.SelectedIndex];
var data =
dataGridShots
.Rows
.Select
(
i =>
new
{
TaskName = i.Cells[0].Value.ToString(),
TaskDescription = i.Cells[1].Value.ToString(),
TaskPriority = i.Cells[2].Value.ToString()
}
)
.ToArray();
var result =
await Task.Run
(
() =>
foreach (var row in data)
handleRowData(row.TaskName, row.TaskDescription, row.TaskPriority, todoList)
);
既然你已经走到这一步,你可能会注意到让你的 postJson
方法异步也不难(有很多方法可以异步发出 HTTP 请求)- 这将允许您在不阻塞任何线程的情况下使整个代码异步。
多线程困难。始终尝试使用尽可能高的抽象,并避免任何共享状态。如果你 do 需要共享状态,你需要同步每个线程对它的每次访问 - 尽量避免这种情况(一个好的做法是让方法在不同的线程上执行static,这样你就不会不小心触及到共享状态)。
根据您的描述,DataGrid
组件的访问似乎不是 的问题。也就是说,这些语句似乎被正确执行,并且 addTaskPair()
方法被成功调用,但 postJSON()
方法没有。
鉴于此,我怀疑 cmbTrackingList.SelectedIndex
的评估是抛出异常并中断线程的原因。
也就是说,建议仍然是一样的:将与 UI 相关的内容保留在 UI 线程中,仅 运行 其他内容在 [=153= 之外] 线。鉴于您发布的代码,似乎唯一真正应该是异步的(即 运行 在后台,以免延迟 UI 线程太多)是调用至 postJSON()
。据推测这是一个同步网络调用,因此可能需要一段时间。其他内容应该 运行 确定且快速。
鉴于 ,以下是我重构代码的方式,利用新的 async
/await
功能:
private async void btnAddShots_Click(object sender, EventArgs e)
{
int noOfShots = dataGridShots.Rows.Count - 1;
int count = 0;
while (count < noOfShots)
{
await addTaskPair(dataGridShots.Rows[count].Cells[0].Value.ToString(),
dataGridShots.Rows[count].Cells[1].Value.ToString(),
dataGridShots.Rows[count].Cells[2].Value.ToString());
count += 1;
}
}
private async Task addTaskPair(string taskName, string taskDescription, string taskPriority)
{
try
{
TaskData trackingTask = new TaskData();
trackingTask.content = taskName;
trackingTask.description = taskDescription;
trackingTask.priority = taskPriority;
string trackingJson = JsonConvert.SerializeObject(trackingTask);
trackingJson = "{ \"todo-item\":" + trackingJson + " }";
string jsonResponse;
string url = teamworkURL + "/tasklists/"
+ todoLists.todoLists[cmbTrackingList.SelectedIndex].id + "/tasks.json";
jsonResponse = await Task.Run(() => postJSON(trackingJson, url));
}
catch (Exception e)
{
debugMessage(e.ToString());
}
}
注意:在上面我把你自己的名字Task
改成了TaskData
。我 强烈 建议您选择 Task
以外的名称,因为在整个现代 .NET API 中普遍使用 .NET Task
类型.
在上面,大部分代码会在UI线程上运行。 async
方法在任何 await
语句中被编译器重写为 return,并在等待的 Task
完成时恢复执行该方法。请注意,当最终有一个 Task
对象等待时,async
方法仅 returns;所以在上面,一旦 addTaskPair()
方法调用了 Task.Run()
并且它本身在 await
处有 returned,btnAddShots_Click()
方法最初将 return声明。
重要:在此上下文中,从 UI 线程调用和等待异步方法会导致框架 运行 方法的其余部分 返回到 UI线程。也就是说,当异步操作完成后,代码执行的控制权 return 返回到您开始的 UI 线程。
正是这个功能使所有这些功能都能正常工作,因此确保您理解它很有用。 :)
对 postJSON()
的调用在单独的线程中执行,使用由 Task.Run()
方法创建的 Task
对象。由于它将在 UI 线程以外的其他线程上执行,因此我已将其 URL 参数的计算移动到局部变量,就在调用 Task.Run()
之前,然后传递该变量在任务线程中调用 postJSON()
方法。这样做可以确保 cmbTrackingList.SelectedIndex
的计算在 UI 线程中完成。
编辑:
注意到 OP 评论说他使用的是 .NET 4 而不是 4.5(其中正式发布了 async
/await
功能),我提供了这个稍微尴尬的替代方案,仍然保留了上述 4.5 兼容版本的执行特性。虽然可以在 VS2010 上安装 async
/await
功能(恕我直言,这是一种更好的方法),但这种替代方法允许 "pure" .NET 4 代码,同时仍然实现基本相同的 运行时间结果。
private void btnAddShots_Click(object sender, EventArgs e)
{
Action<Task> continuation = null;
TaskScheduler uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
int noOfShots = dataGridShots.Rows.Count - 1;
int count = 0;
// Note that the continuation delegate is chained, attaching itself as
// the continuation for each successive task, thus achieving a looping
// mechanism.
continuation = task =>
{
if (count < noOfShots)
{
addTaskPair(dataGridShots.Rows[count].Cells[0].Value.ToString(),
dataGridShots.Rows[count].Cells[1].Value.ToString(),
dataGridShots.Rows[count].Cells[2].Value.ToString())
.ContinueWith(continuation, uiScheduler);
count += 1;
}
}
// Invoking the continuation delegate directly gets the ball rolling
continuation(null);
}
private Task addTaskPair(string taskName, string taskDescription, string taskPriority)
{
try
{
TaskData trackingTask = new TaskData();
trackingTask.content = taskName;
trackingTask.description = taskDescription;
trackingTask.priority = taskPriority;
string trackingJson = JsonConvert.SerializeObject(trackingTask);
trackingJson = "{ \"todo-item\":" + trackingJson + " }";
string url = teamworkURL + "/tasklists/"
+ todoLists.todoLists[cmbTrackingList.SelectedIndex].id + "/tasks.json";
// NOTE: must explicitly specify TaskScheduler.Default, because
// the default scheduler in the context of a Task is whatever the
// current scheduler is, which while executing a continuation would
// be the UI scheduler, not TaskScheduler.Default.
return Task.Factory.StartNew(() => postJSON(trackingJson, url),
CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default)
.ContinueWith(task =>
{
if (task.Exception != null)
{
// Task exceptions are wrapped in an AggregateException
debugMessage(task.Exception.InnerException.ToString());
}
else
{
string jsonResponse = task.Result;
// do something with jsonResponse?
}
}, TaskScheduler.FromCurrentSynchronizationContext());
}
catch (Exception e)
{
debugMessage(e.ToString());
}
}
备注:
- 当前同步上下文的任务计划程序用于每个延续。这确保延续本身在 UI 线程上执行,您可以在其中安全地与 UI 对象交互。
- 恕我直言,最尴尬的部分是您的
while
循环变成了 if
语句,因为循环的执行跨越多个方法调用。
- 虽然
async
/await
通常允许正常的异常处理语法,但在使用显式延续时您没有此选项。但是 Task
将包装在 AggregateException
实例中发生的任何异常,您可以使用它来获取真正的异常并报告它。
TaskFactory.StartNew()
有一些微妙的行为:它用于 运行 给定任务的调度程序是 TaskScheduler.Current
。第一次调用 addTaskPair()
方法时,没有当前任务,因此 TaskScheduler.Current
return 是默认(即线程池)调度程序。但是每次后续调用 addTaskPair()
方法时,它都是从任务延续中调用的,因此 TaskScheduler.Current
将 return 用于执行延续的调度程序。当然,我们有意将其设为 UI 调度程序,而 运行 使用该调度程序来执行新的 postJSON()
任务将达不到目的,因为它只会在当前线程上同步执行。所以这里势在必行指定我们想要的调度器,即TaskScheduler.Default
,对应线程池调度器
如果没有 async
/await
,就很难把事情做好。它在语法上更冗长,但恕我直言,它仍然是一个相当不错的选择,因为它基本上保留了所需的命令式代码结构。特别是,您可以在 UI 线程中保持执行流程,使 UI 对象的访问变得微不足道,并且仅在需要时分叉 long-运行ning 操作.
(我还应该指出,这个 .NET 4 版本严格来说并不是编译器在使用 async
/await
时为您生成的版本。它非常相似,但不完全是相同。另外,我要指出的是,虽然以这种方式使用单个 await
实现一个方法还不错,但如果您希望在同一方法中进行多个延续,它会有点失控。这是可能的,但是那时我认为升级到最新版本的 C# 的冲动会非常引人注目 :))。
终于……
如果在完成上述所有操作后,您想坚持使用 BackgroundWorker
,应该可以通过对代码进行相对简单的更改来避免发生的异常:
int selectedIndex = (int)Invoke((Func<int>)(() => cmbTrackingList.SelectedIndex));
jsonResponse = postJSON(trackingJson, teamworkURL + "/tasklists/"
+ todoLists.todoLists[selectedIndex].id + "/tasks.json");
即只需使用 Control.Invoke()
方法在 UI 线程上调用一个匿名方法,该方法将 return cmbTrackingList.SelectedIndex
的值。 Control.Invoke()
方法将接收 returned 值,然后 return 将其返回给您。由于 Control.Invoke()
是通用的,因此它的 object
return 类型必须转换为您知道正在 returned 的类型。
这确保 cmbTrackingList
对象仅在 UI 线程上被访问。我还会注意到,如果在后台处理正在进行时此索引预计不会更改(或者特别是 not 应该),那么另一种选择是检索值在您的 btnAddShots_Click()
方法中,然后将其传递给 DoWork
事件处理程序,后者又会将其传递给需要它的 addTaskPair()
方法。
我把这个选项放在最后是因为我真的相信学习 async
/await
功能是重要和值得的,而 BackgroundWorker
class 已经服务多年来,我们基本上已经被新功能弃用了。但我也欣然承认 BackgroundWorker
仍然是一种很好的做事方式,并且可以在您的场景中发挥作用。
我正在尝试使用 BackgroundWorker
来完成任务。我已经让工作人员正确地 运行,在 DoWork
方法下它然后调用另一个执行的方法但后来我遇到了我的问题:当该方法试图调用另一个方法时它没有成功并且没有抛出一个异常,我只能认为这是我在 BackgroundWorker
上做错的事情,因为当 运行 在 UI 线程上测试方法按预期执行时。
这是我 运行 我的工人所在的地方:
private void btnAddShots_Click(object sender, EventArgs e)
{
backgroundWorker.RunWorkerAsync();
}
这是我的 DoWork
方法:
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
int noOfShots = dataGridShots.Rows.Count - 1;
int count = 0;
while (count < noOfShots)
{
addTaskPair(dataGridShots.Rows[count].Cells[0].Value.ToString(),
dataGridShots.Rows[count].Cells[1].Value.ToString(),
dataGridShots.Rows[count].Cells[2].Value.ToString());
count += 1;
}
}
这是我的工作人员调用的 addTaskPair
方法的精简版本:
private void addTaskPair(string taskName, string taskDescription, string taskPriority)
{
try
{
Task trackingTask = new Task();
trackingTask.content = taskName;
trackingTask.description = taskDescription;
trackingTask.priority = taskPriority;
string trackingJson = JsonConvert.SerializeObject(trackingTask);
trackingJson = "{ \"todo-item\":" + trackingJson + " }";
string jsonResponse;
jsonResponse = postJSON(trackingJson, teamworkURL + "/tasklists/"
+ todoLists.todoLists[cmbTrackingList.SelectedIndex].id + "/tasks.json");
}
catch (Exception e)
{
debugMessage(e.ToString());
}
}
在上面的示例中,您会看到我调用了方法 postJSON
,这是我遇到困难的地方。通过测试我已经验证了上面的方法运行s,但是postJSON
方法在这个线程中被调用时根本没有运行。
我在研究这个问题时看到了很多对 Invoking
的引用,但它们似乎都适用于更改 ui 控件,而我不需要这样做(尽管进度条是使用ProgressChanged
BackgroundWorker
事件)。
如果需要,我可以进一步澄清我的问题,但我真的希望得到这方面的帮助,因为我以前从未成功地使用过 backgroundworker 或线程(我不是专业人士,我相信你可以从我的代码)。
您正在使用来自 BackgroundWorker.DoWork
事件处理程序的 UI 控件。不要那样做。
在启动 BackgroundWorker
之前收集数据,并将其作为参数传递给 RunWorkerAsync
方法。不要触摸 BackgroundWorker.DoWork
中的 UI - 通过 ReportProgress
方法更新进度是可以的。
此外,如果您 运行 使用 .NET 4.5+,您可能需要考虑改用新的 Task
模式。它仍然需要您事先收集数据进行处理,但它更容易使用:
(EDIT:正如 Peter 所建议的,无效访问发生在 cmbTrackingList.SelectedIndex
中;我已将其包含在下面的代码中。这正是我建议的原因对在单独线程中发生的操作使用 static
方法 - 这会让您对正在处理的数据有更多的思考)
var todoList = todoLists.todoLists[cmbTrackingList.SelectedIndex];
var data =
dataGridShots
.Rows
.Select
(
i =>
new
{
TaskName = i.Cells[0].Value.ToString(),
TaskDescription = i.Cells[1].Value.ToString(),
TaskPriority = i.Cells[2].Value.ToString()
}
)
.ToArray();
var result =
await Task.Run
(
() =>
foreach (var row in data)
handleRowData(row.TaskName, row.TaskDescription, row.TaskPriority, todoList)
);
既然你已经走到这一步,你可能会注意到让你的 postJson
方法异步也不难(有很多方法可以异步发出 HTTP 请求)- 这将允许您在不阻塞任何线程的情况下使整个代码异步。
多线程困难。始终尝试使用尽可能高的抽象,并避免任何共享状态。如果你 do 需要共享状态,你需要同步每个线程对它的每次访问 - 尽量避免这种情况(一个好的做法是让方法在不同的线程上执行static,这样你就不会不小心触及到共享状态)。
根据您的描述,DataGrid
组件的访问似乎不是 的问题。也就是说,这些语句似乎被正确执行,并且 addTaskPair()
方法被成功调用,但 postJSON()
方法没有。
鉴于此,我怀疑 cmbTrackingList.SelectedIndex
的评估是抛出异常并中断线程的原因。
也就是说,建议仍然是一样的:将与 UI 相关的内容保留在 UI 线程中,仅 运行 其他内容在 [=153= 之外] 线。鉴于您发布的代码,似乎唯一真正应该是异步的(即 运行 在后台,以免延迟 UI 线程太多)是调用至 postJSON()
。据推测这是一个同步网络调用,因此可能需要一段时间。其他内容应该 运行 确定且快速。
鉴于 ,以下是我重构代码的方式,利用新的 async
/await
功能:
private async void btnAddShots_Click(object sender, EventArgs e)
{
int noOfShots = dataGridShots.Rows.Count - 1;
int count = 0;
while (count < noOfShots)
{
await addTaskPair(dataGridShots.Rows[count].Cells[0].Value.ToString(),
dataGridShots.Rows[count].Cells[1].Value.ToString(),
dataGridShots.Rows[count].Cells[2].Value.ToString());
count += 1;
}
}
private async Task addTaskPair(string taskName, string taskDescription, string taskPriority)
{
try
{
TaskData trackingTask = new TaskData();
trackingTask.content = taskName;
trackingTask.description = taskDescription;
trackingTask.priority = taskPriority;
string trackingJson = JsonConvert.SerializeObject(trackingTask);
trackingJson = "{ \"todo-item\":" + trackingJson + " }";
string jsonResponse;
string url = teamworkURL + "/tasklists/"
+ todoLists.todoLists[cmbTrackingList.SelectedIndex].id + "/tasks.json";
jsonResponse = await Task.Run(() => postJSON(trackingJson, url));
}
catch (Exception e)
{
debugMessage(e.ToString());
}
}
注意:在上面我把你自己的名字Task
改成了TaskData
。我 强烈 建议您选择 Task
以外的名称,因为在整个现代 .NET API 中普遍使用 .NET Task
类型.
在上面,大部分代码会在UI线程上运行。 async
方法在任何 await
语句中被编译器重写为 return,并在等待的 Task
完成时恢复执行该方法。请注意,当最终有一个 Task
对象等待时,async
方法仅 returns;所以在上面,一旦 addTaskPair()
方法调用了 Task.Run()
并且它本身在 await
处有 returned,btnAddShots_Click()
方法最初将 return声明。
重要:在此上下文中,从 UI 线程调用和等待异步方法会导致框架 运行 方法的其余部分 返回到 UI线程。也就是说,当异步操作完成后,代码执行的控制权 return 返回到您开始的 UI 线程。
正是这个功能使所有这些功能都能正常工作,因此确保您理解它很有用。 :)
对 postJSON()
的调用在单独的线程中执行,使用由 Task.Run()
方法创建的 Task
对象。由于它将在 UI 线程以外的其他线程上执行,因此我已将其 URL 参数的计算移动到局部变量,就在调用 Task.Run()
之前,然后传递该变量在任务线程中调用 postJSON()
方法。这样做可以确保 cmbTrackingList.SelectedIndex
的计算在 UI 线程中完成。
编辑:
注意到 OP 评论说他使用的是 .NET 4 而不是 4.5(其中正式发布了 async
/await
功能),我提供了这个稍微尴尬的替代方案,仍然保留了上述 4.5 兼容版本的执行特性。虽然可以在 VS2010 上安装 async
/await
功能(恕我直言,这是一种更好的方法),但这种替代方法允许 "pure" .NET 4 代码,同时仍然实现基本相同的 运行时间结果。
private void btnAddShots_Click(object sender, EventArgs e)
{
Action<Task> continuation = null;
TaskScheduler uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
int noOfShots = dataGridShots.Rows.Count - 1;
int count = 0;
// Note that the continuation delegate is chained, attaching itself as
// the continuation for each successive task, thus achieving a looping
// mechanism.
continuation = task =>
{
if (count < noOfShots)
{
addTaskPair(dataGridShots.Rows[count].Cells[0].Value.ToString(),
dataGridShots.Rows[count].Cells[1].Value.ToString(),
dataGridShots.Rows[count].Cells[2].Value.ToString())
.ContinueWith(continuation, uiScheduler);
count += 1;
}
}
// Invoking the continuation delegate directly gets the ball rolling
continuation(null);
}
private Task addTaskPair(string taskName, string taskDescription, string taskPriority)
{
try
{
TaskData trackingTask = new TaskData();
trackingTask.content = taskName;
trackingTask.description = taskDescription;
trackingTask.priority = taskPriority;
string trackingJson = JsonConvert.SerializeObject(trackingTask);
trackingJson = "{ \"todo-item\":" + trackingJson + " }";
string url = teamworkURL + "/tasklists/"
+ todoLists.todoLists[cmbTrackingList.SelectedIndex].id + "/tasks.json";
// NOTE: must explicitly specify TaskScheduler.Default, because
// the default scheduler in the context of a Task is whatever the
// current scheduler is, which while executing a continuation would
// be the UI scheduler, not TaskScheduler.Default.
return Task.Factory.StartNew(() => postJSON(trackingJson, url),
CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default)
.ContinueWith(task =>
{
if (task.Exception != null)
{
// Task exceptions are wrapped in an AggregateException
debugMessage(task.Exception.InnerException.ToString());
}
else
{
string jsonResponse = task.Result;
// do something with jsonResponse?
}
}, TaskScheduler.FromCurrentSynchronizationContext());
}
catch (Exception e)
{
debugMessage(e.ToString());
}
}
备注:
- 当前同步上下文的任务计划程序用于每个延续。这确保延续本身在 UI 线程上执行,您可以在其中安全地与 UI 对象交互。
- 恕我直言,最尴尬的部分是您的
while
循环变成了if
语句,因为循环的执行跨越多个方法调用。 - 虽然
async
/await
通常允许正常的异常处理语法,但在使用显式延续时您没有此选项。但是Task
将包装在AggregateException
实例中发生的任何异常,您可以使用它来获取真正的异常并报告它。 TaskFactory.StartNew()
有一些微妙的行为:它用于 运行 给定任务的调度程序是TaskScheduler.Current
。第一次调用addTaskPair()
方法时,没有当前任务,因此TaskScheduler.Current
return 是默认(即线程池)调度程序。但是每次后续调用addTaskPair()
方法时,它都是从任务延续中调用的,因此TaskScheduler.Current
将 return 用于执行延续的调度程序。当然,我们有意将其设为 UI 调度程序,而 运行 使用该调度程序来执行新的postJSON()
任务将达不到目的,因为它只会在当前线程上同步执行。所以这里势在必行指定我们想要的调度器,即TaskScheduler.Default
,对应线程池调度器
如果没有 async
/await
,就很难把事情做好。它在语法上更冗长,但恕我直言,它仍然是一个相当不错的选择,因为它基本上保留了所需的命令式代码结构。特别是,您可以在 UI 线程中保持执行流程,使 UI 对象的访问变得微不足道,并且仅在需要时分叉 long-运行ning 操作.
(我还应该指出,这个 .NET 4 版本严格来说并不是编译器在使用 async
/await
时为您生成的版本。它非常相似,但不完全是相同。另外,我要指出的是,虽然以这种方式使用单个 await
实现一个方法还不错,但如果您希望在同一方法中进行多个延续,它会有点失控。这是可能的,但是那时我认为升级到最新版本的 C# 的冲动会非常引人注目 :))。
终于……
如果在完成上述所有操作后,您想坚持使用 BackgroundWorker
,应该可以通过对代码进行相对简单的更改来避免发生的异常:
int selectedIndex = (int)Invoke((Func<int>)(() => cmbTrackingList.SelectedIndex));
jsonResponse = postJSON(trackingJson, teamworkURL + "/tasklists/"
+ todoLists.todoLists[selectedIndex].id + "/tasks.json");
即只需使用 Control.Invoke()
方法在 UI 线程上调用一个匿名方法,该方法将 return cmbTrackingList.SelectedIndex
的值。 Control.Invoke()
方法将接收 returned 值,然后 return 将其返回给您。由于 Control.Invoke()
是通用的,因此它的 object
return 类型必须转换为您知道正在 returned 的类型。
这确保 cmbTrackingList
对象仅在 UI 线程上被访问。我还会注意到,如果在后台处理正在进行时此索引预计不会更改(或者特别是 not 应该),那么另一种选择是检索值在您的 btnAddShots_Click()
方法中,然后将其传递给 DoWork
事件处理程序,后者又会将其传递给需要它的 addTaskPair()
方法。
我把这个选项放在最后是因为我真的相信学习 async
/await
功能是重要和值得的,而 BackgroundWorker
class 已经服务多年来,我们基本上已经被新功能弃用了。但我也欣然承认 BackgroundWorker
仍然是一种很好的做事方式,并且可以在您的场景中发挥作用。