sOlOHsU's Blogβ

灯火阑珊处

在.NET Compact Framework中使用后台任务

最近在做一个Windows Mobile平台客户端的项目,需要在后台线程中连接服务器并进行消息的发送和接收。

后台任务

由于之前也没接触过C#,所以查找了一下相关资料,发现基本上有使用BackgroundWorker和直接使用Thread两种实现方式。

BackgroundWorker看名字应该类似于Android平台中AsynTask,为了方便编写简单的非UI后台任务而对Thread进行了封装。由于之前的Android客户端中收发消息都是使用的AsynTask,并且后台任务的逻辑也比较简单,BackgroundWorker完全能够满足要求。

写代码时发现没有找到BackgroundWorker这个类,想到Windows Mobile平台使用的是.NET Compact Framework,江湖人称.NET CF,Google了一下,果然CF是不支持BackgroundWorker的,只好另寻他法。

Google之后发现已经有人问过这个问题:

回答中有人建议自己写一个BackgroundWorker,也有人建议参考微软官方的这篇文章:

这篇文章中提到了在.NET CF中创建后台任务的三种方式:

  1. 异步访问Web Service
  2. 线程池
  3. 显示的创建线程

根据我们的应用场景我最终选择了线程池这种比较简单的方式。

1
2
3
4
5
6
7
8
9
10
11
12
13
       void ReadBigFile2(object val)
     {
        string dataFile = (string) val ; // val is a reference to fName
        // Do work to read and process dataFile
     }
  
     public void btnStartRead_Click(object sender, EventArgs e)
     {
        string fName = BigDataFile.xml ;
        WaitCallback w = new WaitCallback(ReadBigFile2) ;
        // fName will be passed to ReadBigFile
        ThreadPool.QueueUserWorkItem(w, fName) ;
     }

查看默认的线程池中辅助线程的最大数目和异步I/O线程的最大数目:

1
2
3
4
   int workerThreads, completionPortThreads;
  ThreadPool.GetMaxThreads(out workerThreads, out completionPortThreads);
  System.Diagnostics.Debug.WriteLine(workerThreads);  // 25
  System.Diagnostics.Debug.WriteLine(completionPortThreads);// 500

可以使用ThreadPool.SetMaxThreads方法按需修改。

在后台任务中操纵UI控件

将消息收发转到后台线程之后,就不能直接在收发消息时直接操纵UI控件了,需要在UI线程中定义好操纵控件的方法,然后在后台线程中使用Control.Invoke或者Control.BeginInvoke方法通过委托的方式进行调用,InvokeBeginInvoke的区别是前者在进行委托调用时后台线程会阻塞,而后者使用的是异步委托调用。详细分析可以参考这几篇博文:

关于WaitCursor

在Android中执行后台任务时,我们通常会在界面上覆盖一个指示后台任务正在运行的ProgressDialog。

C#中可以使用Cursor.Current = Cursors.WaitCursor;来显示一个等待指示器。但是这时我们依然可以对界面中的控件进行操作,简单的解决方法是后台任务开始时使用this.Enabled = false;将当前窗体设为不可用,执行结束后this.Enabled = true;再恢复可用。缺点是整个界面会变成灰色。

参考资料