代码改变世界

多线程

2011-03-18 17:29  三皮开发时  阅读(267)  评论(0编辑  收藏  举报

线程还要分两部分:UI线程和无UI线程,即是否有操作UI控件的动作

UI线程:

先看一个多线程的错误范例:

 

 private Thread myThread;

        
private void Form1_Load(object sender, EventArgs e)

        {
            myThread 
= new Thread(new ThreadStart(RunsOnWorkerThread));
            myThread.Start();
        }

        
private void RunsOnWorkerThread()
        {
            label1.Text 
= "myThread线程调用UI控件";
        }

会报错,线程间操作无效: 从不是创建控件“label1”的线程访问它。

原因是因为,当前访问label1控件的进程不一定是myThread线程,可能是主线程,所以会出现错误,从不是创建该控件的线程访问它

这个时候需要在创建控件的基础句柄所在的线程上操作,也就是要用到委托,以下提供几种方法:

 

 

一、System.Windows.Forms.MethodInvoker 类型是一个系统定义的委托,用于调用不带参数的方法。
      

  private Thread myThread;

        
private void Form1_Load(object sender, EventArgs e)

        {

            myThread 
= new Thread(new ThreadStart(RunsOnWorkerThread));

            myThread.Start();

        }

        
private void RunsOnWorkerThread()

        {

            MethodInvoker mi 
= new MethodInvoker(SetControlsProp);

            BeginInvoke(mi);

        }

        
private void SetControlsProp()

        {

            label1.Text 
= "myThread线程调用UI控件";

        }

 

二、直接用System.EventHandle(可带参数)

       

 private Thread myThread;

        
private void Form1_Load(object sender, EventArgs e)

        {

            myThread 
= new Thread(new ThreadStart(RunsOnWorkerThread));

            myThread.Start();

        }

        
private void RunsOnWorkerThread()

        {

            
//DoSomethingSlow();

            
string pList = "myThread线程调用UI控件";

            label1.BeginInvoke(
new System.EventHandler(UpdateUI), pList);

        }

        
//直接用System.EventHandler,没有必要自定义委托

        
private void UpdateUI(object o, System.EventArgs e)

        {

           
//UI线程设置label1属性

            label1.Text 
= o.ToString() + "成功!";

        }

 

三、包装 Control.Invoke

虽然第二个方法中的代码解决了这个问题,但它相当繁琐。如果辅助线程希望在结束时提供更多的反馈信息,而不是简单地给出“Finished!”消息,则 BeginInvoke 过于复杂的使用方法会令人生畏。为了传达其他消息,例如“正在处理”、“一切顺利”等等,需要设法向 UpdateUI 函数传递一个参数。可能还需要添加一个进度栏以提高反馈能力。这么多次调用 BeginInvoke 可能导致辅助线程受该代码支配。这样不仅会造成不便,而且考虑到辅助线程与 UI 的协调性,这样设计也不好。对这些进行分析之后,我们认为包装函数可以解决这两个问题。

       

        private Thread myThread;
        
private void Form1_Load(object sender, EventArgs e)
        {
            myThread 
= new Thread(new ThreadStart(RunsOnWorkerThread));
            myThread.Start();
        }

        
private void RunsOnWorkerThread()
        {
            
////DoSomethingSlow();
            
for (int i = 0; i < 100; i++)
            {
                ShowProgress( Convert.ToString(i)
+"%", i);
                Thread.Sleep(
100);
            }
        }

        
public void ShowProgress(string msg, int percentDone)
        {
            
// Wrap the parameters in some EventArgs-derived custom class:
            System.EventArgs e 
= new MyProgressEvents(msg, percentDone);
            
object[] pList = { this, e };
            BeginInvoke(
new MyProgressEventsHandler(UpdateUI), pList);
        }

        
private delegate void MyProgressEventsHandler(object sender, MyProgressEvents e);

        private void UpdateUI(object sender, MyProgressEvents e)
        {
            lblStatus.Text 
= e.Msg;
            myProgressControl.Value 
= e.PercentDone;
       }

      
public class MyProgressEvents : EventArgs
       {
        
public string Msg;
        
public int PercentDone;

        
public MyProgressEvents(string msg, int per)
        {
            Msg 
= msg;
            PercentDone 
= per;
        }
}

 

ShowProgress 方法对将调用引向正确线程的工作进行封装。这意味着辅助线程代码不再担心需要过多关注 UI 细节,而只要定期调用 ShowProgress 即可。

如果我提供一个设计为可从任何线程调用的公共方法,则完全有可能某人会从 UI 线程调用这个方法。在这种情况下,没必要调用 BeginInvoke,因为我已经处于正确的线程中。调用 Invoke 完全是浪费时间和资源,不如直接调用适当的方法。为了避免这种情况,Control 类将公开一个称为 InvokeRequired 的属性。这是“只限 UI 线程”规则的另一个例外。它可从任何线程读取,如果调用线程是 UI 线程,则返回假,其他线程则返回真。这意味着我可以按以下方式修改包装:

     

   public void ShowProgress(string msg, int percentDone)
   {
       
if (InvokeRequired)
       {
              
// As before
             
//...
         }
        
else
        {
            
// We're already on the UI thread just
            
// call straight through.
            UpdateUI(
thisnew MyProgressEvents(msg,PercentDone));
         }

    }

 

 

无UI:

 

 static void Main(string[] args)
{
            Console.WriteLine(
"Now main thread begin");
            Program p
=new Program();
            Thread t
= new Thread(new ThreadStart(p.ThreadProc));
            t.Start();
           
for (int i = 0; i < 10; i++)
            {
                Console.WriteLine(
"Main Thread:{0}",i);
                Thread.Sleep(
0);//这个的意思只不过是让当前线程停顿一下,使得其他线程和他再次重新抢占资源
            }
            Console.Read();
        }
      
void ThreadProc()
        {
           
for (int i = 0; i < 10; i++)
            {
                Console.WriteLine(
"ThreadProc:{0}",i);
                Thread.Sleep(0);
            }
        }

 

 

 

 

可以看到 当主线程执行了一次循环时,Sleep(0)了一下,使得资源重新在两个线程中分配了一下。另一个线程抢占到资源并执行。

 

再执行一下,看下结果:

 

 这次重新分配资源时,先是主线程抢到了资源执行一段时间,之后是另一个线程执行。

从执行结果可以看出,Thread.Sleep(0) 的真正作用,同时也可以看出多线程之间是交替执行的。

 

摘:

http://blog.csdn.net/jackey0517/archive/2009/09/08/4533458.aspx

http://www.cnblogs.com/ATually/archive/2010/10/21/1857261.html

http://blog.csdn.net/knight94/archive/2006/03/16/626584.aspx

 

注:如果没有对UI控件的操作,不需用BeginInvoke、Invoke方法,会导致假死,另外在循环体内加个Thread.Spleed(),让CPU闲置一会儿,会好很多

以下是一个无UI控件线程的例子:

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;


namespace WindowsFormsApplication1
{
    
    
public class clsSubThread:IDisposable
    {

        
private Thread thdSubThread = null;
        
private Mutex mUnique = new Mutex();

        
//是否停止
        private bool blnIsStopped;
        
//是否悬挂、暂停
        private bool blnSuspended;
        
//是否开始
        private bool blnStarted;

        
private int nStartNum;

        
public bool IsStopped
        {
            
getreturn blnIsStopped; }
        }
        
public bool IsSuspended
        {
            
getreturn blnSuspended; }
        }
        
public int ReturnValue
        {
            
getreturn nStartNum;}
        }
     
        
public clsSubThread( int StartNum )
        {
            
//
            
// TODO: Add constructor logic here
            
//
            blnIsStopped = true;
            blnSuspended 
= false;
            blnStarted 
= false;
            
            nStartNum 
= StartNum;
        }
 
        
/// <summary>
        
/// Start sub-thread   子线程开始
        
/// </summary>
        public void Start()
        {
            
if!blnStarted )
            {
                thdSubThread 
= new Thread( new ThreadStart( SubThread ) );
                blnIsStopped 
= false;
                blnStarted 
= true;
                thdSubThread.Start();
            }
        }
 
        
/// <summary>
        
/// Thread entry function  线程进入方法
        
/// </summary>
        private void SubThread()
        {
            
do
            {
                
// Wait for resume-command if got suspend-command here  
                mUnique.WaitOne();
                mUnique.ReleaseMutex();
                nStartNum
++;            
                Thread.Sleep(
1000); // Release CPU here
            }while( blnIsStopped == false );
        }
 
        
/// <summary>
        
/// Suspend sub-thread  挂起线程
        
/// </summary>
        public void Suspend()
        {
            
if( blnStarted && !blnSuspended )
            {
                blnSuspended 
= true;
                mUnique.WaitOne();
            }
        }
    
        
/// <summary>
        
/// Resume sub-thread  继续线程
        
/// </summary>
        public void Resume()
        {
            
if( blnStarted && blnSuspended )
            {
                blnSuspended 
= false;
                mUnique.ReleaseMutex();
            }
        }
 
        
/// <summary>
        
/// Stop sub-thread  停止线程
        
/// 
        
/// </summary>
        public void Stop()
        {
            
if( blnStarted )
            {
                
if( blnSuspended )
                    Resume();
 
                blnStarted 
= false;
                blnIsStopped 
= true;
                thdSubThread.Join();
            }
        }


        
#region IDisposable 成员

        
public void Dispose()
        {
            
// TODO:  Add clsSubThread.Dispose implementation
            Stop();//Stop thread first
            GC.SuppressFinalize(this);

        }

        
#endregion
    }
}