C#绝对新手之C#中的多线程小结
大概有4种方法:
Dispatcher、异步委托、手动多线程、BackgroundWorker,另外还有一个DispatcherTimer,是定时器。
其中Dispatcher与DispatcherTimer相同,是利用在主线程进行任务优先级的排列来模拟多线程,因此其中实质是单线程
,所以大负荷的运算不宜用它们。在异步调用的时候,使用Dispatcher.Invoke(addItem, image);
BackgroundWorker会到线程池去抓一个线程作为工作线程,完成工作后切换回 UI 线程调用你定义的处理完成工作的委
托。每计算出一次值,就会用worker.ReportProgress(percentComplete)通知回调函数,并可在回调函数中更新UI。特
别适合需要更新已完成的百分比的场景。
这些都可以和数据绑定结合起来。
①异步委托还是手动多线程
异步委托:支持线程结束时回调(BeginInvoke方法的的第一个参数),但不支持在外部中断线程,多用于更新UI。
手动线程:支持在外部中断线程(Thread.Abort方法),但不易得到函数的返回值。另外还不能更新UI。因为“Windows
窗体”使用单线程单元 (STA) 模型(WPF窗体也一样),STA模型意味着可以在任何线程上创建窗口,但窗口一旦创建后
就不能切换线程,并且对它的所有函数调用都必须在其创建线程上发生。特别是在注册事件的回调函数中要小心。
委托的用法:
[c-sharp] view plaincopy
- public class MyDelegateTest
- {
- // 步骤1,声明delegate对象
- public delegate bool MyDelegate(string name);
- // 这是我们欲传递的方法,它与MyDelegate具有相同的参数和返回值类型
- public static bool MyDelegateFunc(string name)
- {
- Console.WriteLine("Hello, {0}", name);
- return ture;
- }
- public static voidMain ()
- {
- // 步骤2,创建delegate对象
- MyDelegate md = new MyDelegate(MyDelegateTest.MyDelegateFunc);
- // 步骤3,调用delegate
- Console.WriteLine("Result is {0}", md("sam1111"));
- }
- }
这种调用方法和用委托对象的Invoke一样,都是同步调用,会阻塞当前线程。异步调用需要用BeginInvoke:
[c-sharp] view plaincopy
- class Program
- {
- private static int newTask(int ms)
- {
- Console.WriteLine("任务开始");
- Thread.Sleep(ms);
- Random random = new Random();
- int n = random.Next(10000);
- Console.WriteLine("任务完成");
- return n;
- }
- private delegate int NewTaskDelegate(int ms);
- static void Main(string[] args)
- {
- NewTaskDelegate task = newTask;
- IAsyncResult asyncResult = task.BeginInvoke(2000, null, null); // EndInvoke方法将被阻塞2
- 秒
- //Do Something else
- int result = task.EndInvoke(asyncResult);
- Console.WriteLine(result);
- }
- }
这里的BeginInvoke就是异步调用,运行后立即返回,不会引起当前线程的阻塞。但是在本例程中,因为newTask中要
Sleep 2秒中,如果Do Something else的时间没有2秒的话,EndInvoke还是会引起当前线程的阻塞,因为它要等待
newTask执行完毕。
那能不能不调用EndInvoke,让它自己结束呢?不太好。因为一来BeginInvoke和EndInvoke必须成对调用。即使不需要返
回值,但EndInvoke还是必须调用,否则可能会造成内存泄漏,因为它是利用了线程池资源。二来往往要调用EndInvoke
来获得函数的返回值。
如果是用BeginInvoke来进行轮询操作,EndInvoke是无法返回的,这时可以用一个变量来控制一下:
在我的应用场景里是定义了一个包装类:
[c-sharp] view plaincopy
- class IsNetworkAvailableDelegate
- {
- private IsNetworkAvailableWrapper _available;
- private delegate void MyDelegate();
- private MyDelegate _dele;
- private IAsyncResult _result;
- private bool _running = true;
- private void TryConnect()
- {
- while (this._running)
- {
- try
- {
- Ping _ping = new Ping();
- if (_ping.Send("www.baidu.com").Status == IPStatus.Success)
- this._available.IsNetworkAvailable = true;
- else
- this._available.IsNetworkAvailable = false;
- }
- catch
- {
- this._available.IsNetworkAvailable = false;
- }
- Thread.Sleep(500);
- }
- }
- public IsNetworkAvailableDelegate(IsNetworkAvailableWrapper available)
- {
- this._available = available;
- this._dele = new MyDelegate(this.TryConnect);
- this._result = this._dele.BeginInvoke(null, null);
- }
- public void EndInvoke()
- {
- this._running = false;
- this._dele.EndInvoke(this._result);
- }
- }
要想完全与当前线程异步,可以利用BeginInvoke的第二个参数,设置一个函数执行完成后的回调函数:
[c-sharp] view plaincopy
- private delegate int MyDelegate(int a);
- private int method(int a)
- {
- Thread.Sleep(10000);
- Console.WriteLine(a);
- return 100;
- }
- private void MethodCompleted(IAsyncResult asyncResult)
- {
- if (asyncResult == null) return;
- textBox1.Text = (asyncResult.AsyncState as MyDelegate).EndInvoke(asyncResult).ToString();
- }
- private void button1_Click(object sender, EventArgs e)
- {
- MyDelegate my = method;
- IAsyncResult asyncResult = my.BeginInvoke(MethodCompleted, my);
- }
这样就可以让当前线程完全没有等待的感觉了。
不过有时候,当前线程又想要利用函数执行的时间干点私活,然后在函数执行完成后再做别的,可以用WaitOne方法:
[c-sharp] view plaincopy
- class Program
- {
- public delegate int BinaryOpDelegate(int x, int y);
- static void Main(string[] args)
- {
- Console.WriteLine("***** Async Delegate Invocation *****");
- // Print out the ID of the executing thread.
- Console.WriteLine("Main() invoked on thread {0}.",
- Thread.CurrentThread.ManagedThreadId);
- // Invoke Add() on a secondary thread.
- BinaryOpDelegate b = new BinaryOpDelegate(Add);
- IAsyncResult iftAR = b.BeginInvoke(10, 10, null, null);
- // This message will keep printing until
- // the Add() method is finished.
- //用WaitOne 等待异步委托执行完成
- while (!iftAR.AsyncWaitHandle.WaitOne(1000, true))
- {
- Console.WriteLine("Doing more work in Main()!");
- }
- // Obtain the result of the Add()
- // method when ready.
- int answer = b.EndInvoke(iftAR);
- Console.WriteLine("10 + 10 is {0}.", answer);
- Console.ReadLine();
- }
- #region Very slow addition...
- static int Add(int x, int y)
- {
- // Print out the ID of the executing thread.
- Console.WriteLine("Add() invoked on thread {0}.",
- Thread.CurrentThread.ManagedThreadId);
- 模拟一个花费时间较长的行为
- // Pause to simulate a lengthy operation.
- Thread.Sleep(5000);
- return x + y;
- }
- #endregion
- }
(PS:用lambda语法还可以写出很炫的句子:)
[c-sharp] view plaincopy
- Action<object> action=(obj)=>method(obj);
- action.BeginInvoke(obj,ar=>action.EndInvoke(ar),null);
其实还可以更直接:
[c-sharp] view plaincopy
- new Action<object>((obj) => method(obj)).BeginInvoke(ar=>action.EndInvoke(ar), null);
)
Invoke方法的主要功能就是帮助你在UI线程上调用委托所指定的方法。Invoke方法首先检查发出调用的线程(即当前线程
)是不是UI线程,如果是,直接执行委托指向的方法,如果不是,它将切换到UI线程,然后执行委托指向的方法。
手动线程的调用:
典型的写法是:
[c-sharp] view plaincopy
- public class ProcClass
- {
- private string procParameter = "";
- private string result = "";
- public ProcClass(string parameter)
- {
- procParameter = parameter;
- }
- public void ThreadProc()
- {
- }
- }
- ProcClass threadProc = new ProcClass("use thread class");
- Thread thread = new Thread( new ThreadStart( threadProc.ThreadProc ) );
- thread.IsBackground = true;
- thread.Start();
因为Thread调用的函数只能是无参数且无返回值的,因此通常要用类进行包装。
如果线程既需要可中断,又要与UI打交道:
[c-sharp] view plaincopy
- public class ProcClass
- {
- private string procParameter = "";
- private Form1.OutDelegate delg = null;
- public ProcClass(string parameter, Form1.OutDelegate delg)
- {
- procParameter = parameter;
- this.delg = delg;
- }
- public void ThreadProc()
- {
- delg.BeginInvoke("use ProcClass.ThreadProc()", null, null);
- }
- }
- ProcClass threadProc = new ProcClass("use thread class", new OutDelegate(OutText));
- Thread thread = new Thread( new ThreadStart( threadProc.ThreadProc ) );
- thread.IsBackground = true;
- thread.Start();
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。