.net 中多线程的实现方式

 

一、后台循环任务,少量UI更新。使用backgroundworker组件

backgroundworker是一个多线程组件,可以简单方便实现与UI控件通讯,自动处理多线程冲突。

backgroundworker基本属性

1.      WorkerReportsProgress ,bool:是否允许报告进度;
2.      WorkerSupportsCancellation,bool:是否允许取消线程。
3.      CancellationPending,bool,get:读取用户是否取消该线程。
backgroundworker基本事件
1.      DoWork:工作者线程
2.      RunWorkerCompleted :线程进度报告
3.      ProgressChanged:线程结束报告
backgroundworker基本方法
1.      RunWorkerAsync() :启动工作者线程;
2.      CancelAsync():取消工作者线程;
3.      ReportProgress(int);   报告进度

代码示例:

//启动
private void btnStart_Click(object sender, EventArgs e)
{
    this.btnStart.Enabled = false;
    this.btnStop.Enabled = true;
    this.backgroundWorker.RunWorkerAsync();
}
//通知线程停止
private void btnStop_Click(object sender, EventArgs e)
{
    this.backgroundWorker.CancelAsync();
}
//工作者线程
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
    for (int i = 0; i < 150; i++)
    {
        if (backgroundWorker.CancellationPending)   //查看用户是否取消该线程
        {
            break;
        }
        System.Threading.Thread.Sleep(50);          //干点实际的事
        backgroundWorker.ReportProgress(i);         //报告进度
    }
}
//线程进度报告
private void backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    this.progressBar1.Value = e.ProgressPercentage * 100 / 150;
}
//线程结束报告
private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{           
    this.btnStart.Enabled = true;
    this.btnStop.Enabled = false;
}    
backgroundworker一般用来做UI无关的工作,
对于大量UI更新的工作,采用backgroundworker是可以的,但界面响应仍然会比较卡。

二、不可分割的耗时后台任务,如一个调用一个远程WebService接口或一个长时间执行的查询,不能精确获得进度。这种情况可以使用两个进程,一个工作,一个更新UI(不提供精确进度,只显示动画表示系统运行)

代码示例

Thread uithread = null;

private void btnStart_Click(object sender, EventArgs e)

{

uithread = new Thread(new ThreadStart(this.UpdateProgressThread));

uithread.Start();

Thread workthread = new Thread(new ThreadStart(this.DoSomething));

workthread.Start();

}

private void DoSomething()

{

Thread.Sleep(5000);

uithread.Abort();

MessageBox.Show("work end");

}

private void UpdateProgressThread()

{

for (int i = 0; i < 10000; i++)

{

Thread.Sleep(100);

this.Invoke(new Action<int>(this.UpdateProgress), i);

}

}

private void UpdateProgress(int v)

{

this.progressBar1.Value = v;

}

线程调用的方法都不能访问用户控件,必须通过委托调用Form的方法来实现界面更新。

三、工作压力集中在耗时UI任务,可在工作线程增加延时,让UI线程获得响应时间。整体工作时间增加,用户响应效果会好很多。在耗时循环中更新UI,插入Application.DoEvent会使界面获得响应。

private void FormInitForm_Load(object sender, EventArgs e)

{

this.listView1.Items.Clear();

Thread workthread = new Thread(new ThreadStart(this.DoSomething));

workthread.Start();

}

private void DoSomething()

{

for (int i = 0; i < 30; i++)

{

this.Invoke(new Action<int>(this.LoadPicture), i);

Thread.Sleep(100);

}

}

private void LoadPicture(int i)

{

string text = string.Format("Item{0}", i);

ListViewItem lvi = new ListViewItem(text, 0);

this.listView1.Items.Add(lvi);

Thread.Sleep(200);//模拟耗时UI任务,非循环,不可分解

}

Application.DoEvent代码示例

private void button2_Click(object sender, EventArgs e)

{

for (int i = 0; i < 30; i++)

{

string text = string.Format("Item{0}", i);

ListViewItem lvi = new ListViewItem(text, 0);

this.listView1.Items.Add(lvi);

Thread.Sleep(200);

for (int j = 0; j < 10; j++)

{

Thread.Sleep(10);

Application.DoEvents();

}

}

}

 

四、使用Delegate.BeginInvoke来启动一个函数的异步执行

//声明一个委托,表明需要在子线程上执行的方法的函数签名

delegate double CalculateM(double dim)

//把委托和具体方法关联起来

CalculateM calcm = new CalculateM(calculate)

//使用BeginInvoke开始异步执行

calcm.BeginInvoke(act_dim,new AsyncCallBack(TaskFinished),null);

//在完成时执行的回调函数中取得执行结果,或EndInvoke阻塞主线程的执行,等待异步函数执行完。使用BeginInvoke和EndInvoke进行异步调用,一般有四种方式。

using System;
using System.Threading;

namespace Examples.AdvancedProgramming.AsynchronousOperations
{
    public class AsyncDemo
    {
        // The method to be executed asynchronously.
        public string TestMethod(int callDuration, out int threadId)
        {
            Console.WriteLine("Test method begins.");
            Thread.Sleep(callDuration);
            threadId = Thread.CurrentThread.ManagedThreadId;
            return String.Format("My call time was {0}.", callDuration.ToString());
        }
    }
    // The delegate must have the same signature as the method
    // it will call asynchronously.
    public delegate string AsyncMethodCaller(int callDuration, out int threadId);
}

 

1、使用EndInvoke等待异步调用

异步执行方法最简单的方式是通过调用委托BeginInvoke方法开始执行方法,在主线程执行一些操作,然后调用委托的EndInvoke方法。EndInvoke会阻塞主线程,直到异步调用完成后返回。这种方式一般适用文件或网络操作,但不要在用户界面线程中调用它。

using System;
using System.Threading;

namespace Examples.AdvancedProgramming.AsynchronousOperations
{
    public class AsyncMain
    {
        public static void Main()
        {
            // The asynchronous method puts the thread id here.
            int threadId;

            // Create an instance of the test class.
            AsyncDemo ad = new AsyncDemo();

            // Create the delegate.
            AsyncMethodCaller caller = new AsyncMethodCaller(ad.TestMethod);

            // Initiate the asychronous call.
            IAsyncResult result = caller.BeginInvoke(3000,
                out threadId, null, null);

            Thread.Sleep(0);
            Console.WriteLine("Main thread {0} does some work.",
                Thread.CurrentThread.ManagedThreadId);

            // Call EndInvoke to wait for the asynchronous call to complete,
            // and to retrieve the results.
            string returnValue = caller.EndInvoke(out threadId, result);

            Console.WriteLine("The call executed on thread {0}, with return value \"{1}\".",
                threadId, returnValue);
        }
    }
}

2、使用waitHandle等待异步调用

可以使用BeginInvoke返回的IAsyncResult的AsyncWaitHandle属性获取WaitHandle.异步调用完成时会发出WaitHandle信号,可以调用WaitOne方法等待它。使用WaitHandle在异步调用完成之前或之后,在调用EndInvoke检索结果之前,还可以执行其他处理。

using System;
using System.Threading;

namespace Examples.AdvancedProgramming.AsynchronousOperations
{
    public class AsyncMain
    {
        static void Main()
        {
            // The asynchronous method puts the thread id here.
            int threadId;

            // Create an instance of the test class.
            AsyncDemo ad = new AsyncDemo();

            // Create the delegate.
            AsyncMethodCaller caller = new AsyncMethodCaller(ad.TestMethod);

            // Initiate the asychronous call.
            IAsyncResult result = caller.BeginInvoke(3000,
                out threadId, null, null);

            Thread.Sleep(0);
            Console.WriteLine("Main thread {0} does some work.",
                Thread.CurrentThread.ManagedThreadId);

            // Wait for the WaitHandle to become signaled.
            result.AsyncWaitHandle.WaitOne();

            // Perform additional processing here.
            // Call EndInvoke to retrieve the results.
            string returnValue = caller.EndInvoke(out threadId, result);

            Console.WriteLine("The call executed on thread {0}, with return value \"{1}\".",
                threadId, returnValue);
        }
    }
}

 

 

3、使用轮询异步调用完成

可以使用BeginInvoke返回的IAsyncResult的IsCompleted属性来发现异步调用何时完成。从用户界面服务线程中进行异步调用时,可以执行此操作。轮询完成允许调用线程在异步调用的ThreadPool线程上继续执行。

using System;
using System.Threading;

namespace Examples.AdvancedProgramming.AsynchronousOperations
{
    public class AsyncMain
    {
        static void Main() {
            // The asynchronous method puts the thread id here.
            int threadId;

            // Create an instance of the test class.
            AsyncDemo ad = new AsyncDemo();

            // Create the delegate.
            AsyncMethodCaller caller = new AsyncMethodCaller(ad.TestMethod);

            // Initiate the asychronous call.
            IAsyncResult result = caller.BeginInvoke(3000,
                out threadId, null, null);

            // Poll while simulating work.
            while(result.IsCompleted == false) {
                Thread.Sleep(10);
            }

            // Call EndInvoke to retrieve the results.
            string returnValue = caller.EndInvoke(out threadId, result);

            Console.WriteLine("The call executed on thread {0}, with return value \"{1}\".",
                threadId, returnValue);
        }
    }
}

4、异步调用完成时执行回调方法

启动异步调用的线程不需要处理结果,可以在调用完成时执行回调方法。回调方法在ThreadPool线程上执行。

using System;
using System.Threading;

namespace Examples.AdvancedProgramming.AsynchronousOperations
{
    public class AsyncMain
    {
        // Asynchronous method puts the thread id here.
        private static int threadId;

        static void Main() {
            // Create an instance of the test class.
            AsyncDemo ad = new AsyncDemo();

            // Create the delegate.
            AsyncMethodCaller caller = new AsyncMethodCaller(ad.TestMethod);

            // Initiate the asychronous call.  Include an AsyncCallback
            // delegate representing the callback method, and the data
            // needed to call EndInvoke.
            IAsyncResult result = caller.BeginInvoke(3000,
                out threadId,
                new AsyncCallback(CallbackMethod),
                caller );

            Console.WriteLine("Press Enter to close application.");
            Console.ReadLine();
        }

        // Callback method must have the same signature as the
        // AsyncCallback delegate.
        static void CallbackMethod(IAsyncResult ar)
        {
            // Obtains the last parameter of the delegate call.
           // AsyncMethodCaller caller = (AsyncMethodCaller) ar.AsyncState;

          

          // Obtains return value from the delegate call using EndInvoke.

            AsyncResult aResult = (AsyncResult)ar; 

           AsyncMethodCaller  caller = (AsyncMethodCaller) ar.AsyncDelegate;

            // Call EndInvoke to retrieve the results.
            string returnValue = caller.EndInvoke(out threadId, ar);

            Console.WriteLine("The call executed on thread {0}, with return value \"{1}\".",
                threadId, returnValue);
        }
    }
}

posted @ 2010-05-19 12:04  迷茫中的游魂  阅读(1462)  评论(0编辑  收藏  举报