在C#中我有简单的Winform,当前有一个按钮" start"在click事件中执行一个函数。此功能大约需要20秒。 现在我添加了第二个按钮"停止"设置变量" buttonstopclicked = 1"在点击事件中。 在开始按钮的功能中,我检查此变量,如果设置为1,则退出该功能。
问题是:当c#执行开始按钮事件时,我无法点击停止按钮。
现在的问题是:如何为处理按钮的所有内容添加一个线程?
感谢您的帮助
编辑: 同时我发现了如何创建线程:
Thread myThread = new Thread(new ThreadStart(ButtonThread));
myThread.Start();
private void ButtonThread()
{
while (1 != 0)
{
if (ButtonStartClicked == 1)
{
ButtonStartClicked = 0;
ExecuteFunction();
}
if (ButtonStopClicked == 1)
{
ButtonStopClicked = 0;
}
}
}
但现在我在被调用的函数" ExecuteFunction"中得到一个例外。 InvalidOperationException异常。
从另一个线程使用/创建控件的东西......
我现在能在这做什么? 我正在使用C#2010 express
答案 0 :(得分:2)
按钮在UI线程上运行,您尝试使用自定义线程与UI线程进行交互。对于UI中的响应性,您需要使用后台工作程序。创建后台工作程序并添加要在其Do_Work事件中执行的函数。将以下属性设置为true(其中bw是backgroundworker的名称):
bw.WorkerSupportsCancellation = True
bw.WorkerReportsProgress = True
现在,在开始按钮上,您只需要通过在开始按钮的单击事件中添加此代码来调用后台工作程序:
If Not bw.IsBusy = True Then
bw.RunWorkerAsync()
End If
要取消该过程,您需要在停止按钮的点击事件中添加以下代码:
If bw.WorkerSupportsCancellation = True Then
bw.CancelAsync()
End If
有关详细信息,请浏览以下msdn链接:http://msdn.microsoft.com/en-us/library/cc221403(v=vs.95).aspx
答案 1 :(得分:2)
首先。使用Thread并不是最好的方法。 .Net Framework中提供了更高级别的抽象。你应该(几乎)总是使用语言提供的最高抽象,并将内部结构留给编译器或运行时,因为它们会做得很好。
使用VS 2010,您至少可以访问TPL(任务并行库 - Introduction)
对于取消任务(在引擎盖下使用ThreadPool提供的线程),你应该使用CancellationTokenSource:
private CancellationTokenSource _ctSource = new CancellationTokenSource();
取消按钮的处理程序然后只是设置取消令牌:
private void OnButtonCancelClick(object sender, EventArgs e)
{
_ctSource.Cancel();
}
开始按钮的处理程序可能如下所示:
private void OnButtonStartClick(object sender, EventArgs e)
{
// Start a new task
Task<int> workerTask = Task<int>.Factory.StartNew(
() => PerformLongRunningOperation(_ctSource.Token), _ctSource.Token);
// when the task has finished, display the result (On the GUI Thread!)
workerTask.ContinueWith((task) =>
{
if (task.IsCanceled)
{
MessageBox.Show("Calculation has been canceled.");
return;
}
_LabelResult = task.Result;
}, TaskScheduler.FromCurrentSynchronizationContext); // required to run this part on the GUI thread
}
执行实际工作的方法如下所示:
private int PerformLongRunningOperation(CancellationToken token)
{
for (int i = 0; i < 1000000000; i++)
{
// will cancel the task if the token has been set
token.ThrowIfCancellationRequested();
// otherwise do some stuff
}
return 42;
}
如果您可以访问VS2012和.NetFramework 4.5 / C#5.0,使用async - await,按钮点击处理程序甚至可以缩小为:
private async void OnButtonStartClick(object sender, EventArgs e)
{
try
{
// Await will allow the UI to keep responsive
// Furthermore it will automatically return to the UI SynchronizationContext when done, so updating a label
// has not to be dispatched.
_LabelResult = await Task.Factory.StartNew(
() => PerformLongRunningOperation(_ctSource.Token), _ctSource.Token);
}
catch (TaskCanceledException ex)
{
MessageBox.Show("Calculation has been canceled.");
}
}
有关使用C#进行并行编程的一般性介绍,请访问http://www.albahari.com/threading/part5.aspx#_Parallel_Programming
请记住,异步会导致您需要考虑的许多其他问题。例如,当您的UI响应(它应该是什么)时,用户可以一次又一次地点击开始按钮。因此,在您的场景中,您应该考虑禁用该按钮,直到任务完成或被取消为止,以避免这种情况。