C#笔记19:多线程之线程启动、参数、返回值

作者:Luminji

转自:http://www.cnblogs.com/luminji/archive/2010/09/17/1829333.html


C#笔记19:多线程之线程启动、参数、返回值

本章概要:
1:如何新起线程
2:Thread传参数及取得返回值
3:IsBackground
4:异步调用中的参数和返回值


 

1:如何新起线程

     新起一个线程的方法,可以使用Thread,BackgroundWorker ,ThreadPool,控件.BeginInvoke,委托.BeginInvoke,Timer。
    创建多线程处理应用程序的最可靠方法是使用 BackgroundWorker 组件。但是,当你需要对线程进行精细控制的时候,就需要Thread。总体来说,各种方法各有各的优点,在这里不做细说。

     备注:严格意义上,异步不是新线程。

2:Thread传参数及取得返回值

     Thread的有两个构造函数,其中一个使用参数是ThreadStart,说明该线程在构造函数中不能带入参数。还有一个ParameterizedThreadStart,则可以为你的线程传入参数。还有一个方法是为你的线程方法提供一个包裹类,这也可以解决返回值的问题。不过,这种方法在我看来是最丑陋的写法(参考http://msdn.microsoft.com/zh-cn/library/wkays279.aspx)。
    大部分情况下,一个优良的写法是使用匿名函数,如下:

            int arg1 = 10;
            string arg2 = "argument temp";
            Thread t1 = new Thread(new ThreadStart(delegate
                {
                    MessageBox.Show(arg1.ToString() + arg2);
                }));

     以上的写法,仍然无法解决返回值的问题。如果要获取返回值的,则使用委托或包裹类等其它方法。但以上方法解决了参数的问题。

  

3:IsBackground

      必须注意IsBackground的问题,如果IsBackground为false的,则Windows程序在退出的时候,不会为你自动退出该线程。也就是实际上你的应用程序未结束。

4:异步调用中的参数和返回值

      能完美解决参数和返回值的是使用异步调用的方式。异步调用和Thread相比,一个最大的劣势是不能控制其优先级。
     首先,看代码:
     代码段1:

        public delegate string FuncHandle(int data1, int data2);
        FuncHandle fh ;
            
        private void BT_Temp_Click(object sender, RoutedEventArgs e)
        {
            fh = new FuncHandle(this.Foo);
            AsyncCallback callback = new AsyncCallback(this.AsyncCallbackImpl);
            fh.BeginInvoke(1, 3, callback, null);
        }

        public void AsyncCallbackImpl(IAsyncResult ar)   
        {
            string re = fh.EndInvoke(ar);
            MessageBox.Show(" " + ar.AsyncState);
        }   
  
        string Foo(int data1, int data2)   
        {   
            return " " + data1 + data2;   
        }  

     在异步调用中,如果想在异步的回调函数中,得到异步函数的返回值(如上面代码中的Foo函数的string返回值),则必须要在回调函数中使用EndInvoke(关于EndInvoke会在下文描述)。在上面的例子是如下这句。
     string re = fh.EndInvoke(ar);
     但是,有的时候fh并不见得是个类变量,这个时候,就需要我们将EndInvoke的执行主体由BeginInvoke传递进去。看修改过后的代码片段。
     代码段2:

        public delegate string FuncHandle(int data1, int data2);   
            
        private void BT_Temp_Click(object sender, RoutedEventArgs e)
        {
            FuncHandle fh = new FuncHandle(this.Foo);
            AsyncCallback callback = new AsyncCallback(this.AsyncCallbackImpl);
            fh.BeginInvoke(1, 3, callback, fh);

        }
        public void AsyncCallbackImpl(IAsyncResult ar)   
        {   
            FuncHandle dl = ar.AsyncState as FuncHandle;   
            string re = dl.EndInvoke(ar);
            MessageBox.Show(" " + ar.AsyncState);
        }   
  
        string Foo(int data1, int data2)   
        {   
            return "" + data1 + data2;   
        }  

     通过举一反三,其实BeginInvoke的最后一个参数,可以是任何对象,看具体的应用场景即可。
     下面再介绍一下EndInvoke。EndInvoke在回调函数中,用于承载执行的主体函数的返回值。在另外一个情况下,即上面的代码片段一个简化版本,如下:
     代码段3:

        public delegate string FuncHandle(int data1, int data2);   
        private void BT_Temp_Click(object sender, RoutedEventArgs e)
        {
            FuncHandle fh = new FuncHandle(this.Foo);
            IAsyncResult ar = fh.BeginInvoke(1, 3, null, fh);
            string re = fh.EndInvoke(ar);
            MessageBox.Show(re);
        }
        
        string Foo(int data1, int data2)   
        {   
            return "" + data1 + data2;   
        }  

     我们可以看到,在这个代码片段中,我们根本没有使用回调函数,那么,我们就需要通过EndInvoke来阻滞主线程,使得返回函数主体的返回值。
     再多说一点,调用了 BeginInvoke 后,可以:
    1.进行某些操作,然后调用 EndInvoke 一直阻塞到调用完成。如上文的最后一个代码片段。
    2.使用 IAsyncResult.AsyncWaitHandle 获取 WaitHandle,使用它的 WaitOne 方法将执行一直阻塞到发出 WaitHandle 信号,然后调用EndInvoke。这里主要是主程序等待异步方法,等待异步方法的结果。见代码段4。
    3.轮询由 BeginInvoke 返回的 IAsyncResult,IAsyncResult.IsCompeted确定异步调用何时完成,然后调用 EndInvoke。
    4.将用于回调方法的委托传递给 BeginInvoke。该方法在异步调用完成后在 ThreadPool 线程上执行,它可以调用 EndInvoke。这是在强制装换回调函数里面IAsyncResult.AsyncState(BeginInvoke方法的最后一个参数)成委托,然后用委托执行EndInvoke。即如上代码片段2。
    代码段4:

        public delegate string FuncHandle(int data1, int data2);
        string _sTemp = string.Empty;

        private void BT_Temp_Click(object sender, RoutedEventArgs e)
        {
            FuncHandle fh = new FuncHandle(this.Foo);
            AsyncCallback callback = new AsyncCallback(this.AsyncCallbackImpl);
            IAsyncResult ar = fh.BeginInvoke(1, 3, null, null);
            WaitHandle waitHandle = ar.AsyncWaitHandle;
            waitHandle.WaitOne();
            MessageBox.Show(_sTemp);
        }

        string Foo(int data1, int data2)   
        {
            Thread.Sleep(3000);
            string re = "" + data1 + data2;
            _sTemp = re;
            return re;
        }  
练习:

1.You are developing an application to perform mathematical calculations. You develop a class named     
CalculationValues. You write a procedure named PerformCalculation that operates on an instance of the class. You   
need to ensure that the user interface of the application continues to respond while calculations are being 
performed. You need to write a code segment that calls the PerformCalculation procedure to achieve this goal.    

Which code segment should you use? 
A. private void PerformCalculation() {...}   
private void DoWork()
{  CalculationValues myValues = new CalculationValues();  
Thread newThread = new Thread(       new ThreadStart(PerformCalculation));   
newThread.Start(myValues);}

B. private void PerformCalculation() {...}   
private void DoWork()
{  CalculationValues myValues = new CalculationValues();  
ThreadStart delStart = new        ThreadStart(PerformCalculation);  
Thread newThread = new Thread(delStart);
if (newThread.IsAlive) {newThread.Start(myValues);}} 
C. private void PerformCalculation (CalculationValues values) {...} 
private void DoWork()
{  CalculationValues myValues = new CalculationValues();  
Application.DoEvents();  PerformCalculation(myValues);  
Application.DoEvents();}
D. private void PerformCalculation(object values) {...}   
private void DoWork()
{  CalculationValues myValues = new CalculationValues();  
Thread newThread = new Thread(       new ParameterizedThreadStart(PerformCalculation));   
newThread.Start(myValues);}
Answer: D

2.You are developing an application that will perform mathematical calculations.  You need to ensure that the 
application is able to perform multiple calculations simultaneously. What should you do?     
A. Set the IdealProcessor property of the ProcessThread object.
B. Set the ProcessorAffinity property of the ProcessThread object.
C. For each calculation, call the QueueUserWorkItem method of the ThreadPool class. 
D. Set the Process.GetCurrentProcess().BasePriority property to High.
Answer: C

3.You are developing an application that will perform mathematical calculations.  You need to ensure that the 
application is able to perform multiple calculations simultaneously. What should you do?     
A. Set the IdealProcessor property of the ProcessThread object.
B. Set the ProcessorAffinity property of the ProcessThread object.
C. For each calculation, call the QueueUserWorkItem method of the ThreadPool class. 
D. Set the Process.GetCurrentProcess().BasePriority property to High.
Answer: C

4.Your application uses two threads, named threadOne and threadTwo.  You need to modify the code to prevent the execution of threadOne until threadTwo completes execution.   
What should you do?
A. Configure threadOne to run at a lower priority.
B. Configure threadTwo to run at a higher priority.
C. Use a WaitCallback delegate to synchronize the threads. 
D. Call the Sleep method of threadOne.
E. Call the SpinLock method of threadOne. 
Answer: C


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值