65.9K
CodeProject 正在变化。 阅读更多。
Home

多线程示例

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.49/5 (32投票s)

2007年9月26日

CPOL

3分钟阅读

viewsIcon

177204

downloadIcon

4582

一个展示如何并发运行多个代码并使用 C# 防止跨线程操作错误的示例。

Screenshot - project.jpg

引言

您可能希望在 Windows 窗体上同时执行多个代码,但可能会遇到跨线程操作错误或应用程序死锁。有几种方法可以防止应用程序挂起。在这里,我只想向初学者展示一种简单易行的方法来实现这一目标。

几个月前,我想在应用程序中同时启动 ProgressBar 并执行其他操作。因此,多线程的想法就出现了。当然,您必须使用线程原理分离任务,并防止应用程序挂起。我在网上找到了一些有用的文章,帮助我成功完成了任务。现在,我只想与遇到相同问题的人分享这段小小的经验。

这里有一些我用过的有帮助的链接

进入代码

首先,我们在类的主体中定义一个线程变量。我们希望它是全局的,因为以后会对其进行更改。

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....");
}

暂停/继续

Screenshot - pause.jpg

“暂停”按钮也充当“继续”按钮,用于继续和暂停线程。

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 日——发布原始版本
© . All rights reserved.