多线程示例






4.49/5 (32投票s)
一个展示如何并发运行多个代码并使用 C# 防止跨线程操作错误的示例。

引言
您可能希望在 Windows 窗体上同时执行多个代码,但可能会遇到跨线程操作错误或应用程序死锁。有几种方法可以防止应用程序挂起。在这里,我只想向初学者展示一种简单易行的方法来实现这一目标。
几个月前,我想在应用程序中同时启动 ProgressBar 并执行其他操作。因此,多线程的想法就出现了。当然,您必须使用线程原理分离任务,并防止应用程序挂起。我在网上找到了一些有用的文章,帮助我成功完成了任务。现在,我只想与遇到相同问题的人分享这段小小的经验。
这里有一些我用过的有帮助的链接
- Albahari.com
- 跨线程
- 简单线程
- 更好的线程
- 使用 C# 进行多线程编程
- .NET 2.0 下改进的线程和 UI 集成
- .NET 2.0 中真正的多线程
- DelegateQueue 类
- 使用匿名委托轻松更新 UI
进入代码
首先,我们在类的主体中定义一个线程变量。我们希望它是全局的,因为以后会对其进行更改。
using System.Threading;
...
public Thread thread = null; //assign a default value Delete
删除
这是实现目标的主要部分。这在“删除”按钮中实现。如下所示,我们首先将线程变量定义为全局变量。为了以后停止和启动此线程,“暂停/继续”按钮被使用。在我们的示例中,在“删除”按钮中,我们实际上并没有删除用户浏览的路径中的文件。这只是一个演示,用于描述类似的事情(并发代码执行更为重要)。
private void Btn_Delete_Click(object sender,EventArgs e)
{
if(textBox1.Text ==String.Empty)
{
MessageBox.Show("Please specify a path");
return;
}
Btn_Pause.Enabled = true;
Btn_Stop.Enabled = true;
Btn_Delete.Enabled = false;
InitializeMyObjects();
GetAllFiles();
thread = new Thread(delegate()
{
//Divide thread into several parts.You can execute multiple code
//within a thread back to back:
//Part 1:
//for(int i=0;i<files.Count;Interlocked.Increment(ref i))-for
//understanding atomic operation see www.albahari.com
for(int i = 0; i < files.Count; i++)
{
//accessing any member out of this thread for changing it
//here causes Cross_Thread Opertaion Error.
//this.Text ="something";//---->Cross_Thread Operation Error(Do
//not put here)
this.BeginInvoke((ThreadStart)delegate()
{
this.Text = files[i].ToString().Substring(files[i].LastIndexOf(
'\\') + 1);
//you can delete file by yourself.
this.label2.Text = Convert.ToString(i + 1);
progressBar1.PerformStep();
});
Thread.Sleep(6);
}
//Part 2:
this.BeginInvoke((ThreadStart)delegate()
{
this.Btn_Pause.Enabled = false;
this.Btn_Stop.Enabled = false;
});
//Part 3:
this.BeginInvoke((ThreadStart)delegate()
{
this.ShowMessageDelegate("100% (Process Compeletd)");
});
});//.Start();
thread.Start();
//Implement other codes here for performing them at the time the progressbar
//is being processed .
//e.g;
MessageBox.Show("Am being displayed while progressbar is being performed....");
}
暂停/继续

“暂停”按钮也充当“继续”按钮,用于继续和暂停线程。
private void Btn_Pause_Click(object sender,EventArgs e)
{
if(thread ==null || !thread.IsAlive)
{
MessageBox.Show("Process has not been started or just being finished");
return;
}
if(Btn_Pause.Text.ToUpper() == "pause".ToUpper())
{
Btn_Pause.Text = "Continue";
thread.Suspend();
}
else
{
Btn_Pause.Text = "Pause";
thread.Resume();
}
}
停止
要停止线程,您必须检查此线程是否已暂停。如果是,则使其恢复,然后中止它。在这里,我们还在新线程中实现了进度条的回滚。
private void Btn_Stop_Click(object sender,EventArgs e)
{
if(thread.IsAlive)
{
if(thread.ThreadState ==ThreadState.Suspended)
{
thread.Resume();
}
thread.Abort();
label2.Text = "THIS PROCESS OF DELETING HAS BEEN ABORTED";
label2.Refresh();
new Thread(delegate()
{
int val = this.progressBar1.Value; //--->Don't make mistake,you
//can read but you can not change
//if you put any code that wants
//to modify an object other than
//this thread you'll see
//error.<---
for(int i = 1; i <= val; Interlocked.Increment(ref i))
{
this.BeginInvoke((ThreadStart)delegate()
{
progressBar1.Increment(-1);
});
Thread.Sleep(10);
}
}).Start();
}
else
label2.Text = "PROCESS HAS BEEN ABORETD";
Btn_Pause.Enabled =false;
Btn_Delete.Enabled =false;
Btn_Pause.Text ="Pause";
Btn_Stop.Enabled =false;
return;
}
关注点
当我让进度条运行时,我注意到我无法拖动窗体,因为它挂起了。因此,我为此目的开始了多线程。我使用此代码通过新线程访问同一个 Windows 窗体中的对象。当我第一次创建新线程,然后想要调用一个 void 函数来启动与窗体中对象关联的一些任务时,我得到了错误:“跨线程操作无效:从创建控件‘progressBar1’的线程以外的线程访问控件‘progressBar1’。”
当然,即使通过这种方式,如果您使用除新线程中的对象以外的对象(写入/修改对象)——或者您是在应用程序的主线程中创建的——您仍然会收到此错误。您必须在this.BeginInvoke((ThreadStart)delegate(){//在此处实现您的逻辑…});
部分下使用它,并启动整个新线程。
结论
总而言之,正如我在引言中提到的,有几种方法可以做到这一点。我使用这种方法是因为我认为它易于理解相关的概念,并且非常直接。顺便说一句,我想请您宽容我的文章,因为这是我在 Code Project 上的第一篇文章。
历史
- 2007 年 9 月 26 日——发布原始版本