您的位置:首页 > 编程语言 > C#

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

2014-03-25 19:54 686 查看
作者:Luminji

转自:/article/4723270.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;
}


参考:/article/4688972.html
练习:

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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: