转:C#多线程编程简述

a.启动线程
顾名思义,“启动线程”就是新建并启动一个线程的意思,如下代码可实现:
Thread thread1 = new Thread(new ThreadStart( Count));
其中的 Count 是将要被新线程执行的函数。
b.杀死线程
“杀死线程”就是将一线程斩草除根,为了不白费力气,在杀死一个线程前最好先判断它是否还活着(通过 IsAlive 属性),然后就可以调用 Abort 方法来杀死此线程。
c.暂停线程
它的意思就是让一个正在运行的线程休眠一段时间。如 thread.Sleep(1000); 就是让线程休眠1秒钟。
d.优先级
这个用不着解释了。Thread类中hread PR iority属性,它用来设置优先级,但不能保证 操作系统 会接受该优先级。一个线程的优先级可分为5种:Normal, AboveNormal, BelowNormal, Highest, Lowest。具体实现例子如下:
thread.Priority = ThreadPriority.Highest;
e.挂起线程
Thread类的Suspend方法用来挂起线程,直到调用Resume,此线程才可以继续执行。如果线程已经挂起,那就不会起作用。
if (thread.ThreadState = ThreadState.Running) 
{
thread.Suspend();
}
f.恢复线程
用来恢复已经挂起的线程,以让它继续执行,如果线程没挂起,也不会起作用。
if (thread.ThreadState = ThreadState.Suspended) 
{
thread.Resume();
}


    接下来我们结合线程的ThreadState属性来了解线程的控制。ThreadState是一个枚举类型,它反映的是线程所处的状态。当一个Thread实例刚创建时,它的ThreadState是Unstarted;当此线程被调用Start()启动之后,它的ThreadState是 Running;  在此线程启动之后,如果想让它暂停(阻塞),可以调用Thread.Sleep() 方法,它有两个重载方法(Sleep(int )、Sleep(Timespan )),只不过是表示时间量的格式不同而已,当在某线程内调用此函数时,它表示此线程将阻塞一段时间(时间是由传递给 Sleep 的毫秒数或Timespan决定的,但若参数为0则表示挂起此线程以使其它线程能够执行,指定 Infinite 以无限期阻塞线程),此时它的ThreadState将变为WaitSleepJoin,另外值得注意一点的是Sleep()函数被定义为了static?! 这也意味着它不能和某个线程实例结合起来用,也即不存在类似于t1.Sleep(10)的调用!正是如此,Sleep()函数只能由需“Sleep”的线程自己调用,不允许其它线程调用,正如when to Sleep是个人私事不能由它人决定。但是当某线程处于WaitSleepJoin状态而又不得不唤醒它时,可使用Thread.Interrupt 方法 ,它将在线程上引发ThreadInterruptedException

static void Main(string[] args)

   Thread t1 = new Thread(new ThreadStart(Thread1));
    t1.Start();
      t1.Interrupt ();
     E.WaitOne ();
     t1.Interrupt ();
          t1.Join();
          Console.WriteLine(“t1 is end”);
}
static AutoResetEvent E = new AutoResetEvent(false);
public static void Thread1()
  
   try
   {//从参数可看出将导致休眠
    Thread.Sleep(Timeout.Infinite); 
   }
   catch(System.Threading.ThreadInterruptedException e)
   {//中断处理程序
    Console.WriteLine (" 1st interrupt");
   }
     E.Set ();
   try
   {// 休眠
    Thread.Sleep(Timeout.Infinite ); 
   }
   catch(System.Threading.ThreadInterruptedException e)
   {
     Console.WriteLine (" 2nd interrupt");
   }//暂停10秒
           Thread.Sleep (10000); 
        }
运行结果为:1st interrupt
                  2nd interrupt
                  (10s后)t1 is end


从上例我们可以看出Thread.Interrupt方法可以把程序从某个阻塞(WaitSleepJoin)状态唤醒进入对应的中断处理程序,然后继续往下执行(它的ThreadState也变为Running),此函数的使用必须注意以下几点:
      1、此方法不仅可唤醒由Sleep导致的阻塞,而且对一切可导致线程进入WaitSleepJoin状态的方法(如Wait和Join)都有效。如上例所示, 使用时要把导致线程阻塞的方法放入try块内, 并把相应的中断处理程序放入catch块内。
      2、对某一线程调用Interrupt, 如它正处于WaitSleepJoin状态, 则进入相应的中断处理程序执行, 若此时它不处于WaitSleepJoin状态, 则它后来进入此状态时, 将被立即中断。若在中断前调用几次Interrupt, 只有第一次调用有效, 这正是上例我用同步的原因, 这样才能确保第二次调用Interrupt在第一个中断后调用,否则的话可能导致第二次调用无效(若它在第一个中断前调用)。你可以把同步去掉试试,其结果很可能是:   1st interrupt
      上例还用了另外两个使线程进入WaitSleepJoin状态的方法:利用同步对象和Thread.Join方法。Join方法的使用比较简单,它表示在调用此方法的当前线程阻塞直至另一线程(此例中是t1)终止或者经过了指定的时间为止(若它还带了时间量参数),当两个条件(若有)任一出现,它立即结束WaitSleepJoin状态进入Running状态(可根据.Join方法的返回值判断为何种条件,为true,则是线程终止;false则是时间到)。线程的暂停还可用Thread.Suspend方法,当某线程处于Running状态时对它调用Suspend方法,它将进入SuspendRequested状态,但它并不会被立即挂起,直到线程到达安全点之后它才可以将该线程挂起,此时它将进入Suspended状态。如对一个已处于Suspended的线程调用则无效,要恢复运行只需调用Thread.Resume即可。
      最后我们谈的是线程的销毁,我们可以对需销毁的线程调用Abort方法,它会在此线程上引发ThreadAbortException。我们可把线程内的一些代码放入try块内,并把相应处理代码放入相应的catch块内,当线程正执行try块内代码时如被调用Abort,它便会跳入相应的catch块内执行,执行完catch快内的代码后它将终止(若catch块内执行了ResetAbort则不同了:它将取消当前Abort请求,继续向下执行。所以如要确保某线程终止的最好用Join,如上例)。

二、ThreadPool
线程池(ThreadPool)是一种相对较简单的方法,它适应于一些需要多个线程而又较短任务(如一些常处于阻塞状态的线程) ,它的缺点是对创建的线程不能加以控制,也不能设置其优先级。由于每个进程只有一个线程池,当然每个应用程序域也只有一个线程池(对线),所以你将发现ThreadPool类的成员函数都为static! 当你首次调用ThreadPool.QueueUserWorkItem、ThreadPool.RegisterWaitForSingleObject等,便会创建线程池实例。下面就线程池当中的两函数作一介绍:
public static bool QueueUserWorkItem( //调用成功则返回true
WaitCallback callBack,//要创建的线程调用的委托
        object state //传递给委托的参数
)//它的另一个重载函数类似,只是委托不带参数而已
此函数的作用是把要创建的线程排队到线程池,当线程池的可用线程数不为零时(线程池有创建线程数的限制,缺身值为25),便创建此线程,否则就排队到线程池等到它有可用的线程时才创建。
public static RegisteredWaitHandle RegisterWaitForSingleObject(
   WaitHandle waitObject,// 要注册的 WaitHandle
   WaitOrTimerCallback callBack,// 线程调用的委托
   object state,//传递给委托的参数
   int TimeOut,//超时,单位为毫秒,
   bool executeOnlyOnce file://是否只执行一次
); 
public delegate void WaitOrTimerCallback(
   object state,//也即传递给委托的参数
   bool timedOut//true表示由于超时调用,反之则因为waitObject
);
      此函数的作用是创建一个等待线程,一旦调用此函数便创建此线程,在参数waitObject变为终止状态或所设定的时间TimeOut到了之前,它都处于“阻塞”状态,值得注意的一点是此“阻塞”与Thread的WaitSleepJoin状态有很大的不同:当某Thread处于WaitSleepJoin状态时CPU会定期的唤醒它以轮询更新状态信息,然后再次进入WaitSleepJoin状态,线程的切换可是很费资源的;而用此函数创建的线程则不同,在触发它运行之前,CPU不会切换到此线程,它既不占用CPU的时间又不浪费线程切换时间,但CPU又如何知道何时运行它?实际上线程池会生成一些辅助线程用来监视这些触发条件,一旦达到条件便启动相应的线程,当然这些辅助线程本身也占用时间,但是如果你需创建较多的等待线程时,使用线程池的优势就越加明显。见下例:
static AutoResetEvent ev=new AutoResetEvent(false);
public static int Main(string[] args)
{ ThreadPool.RegisterWaitForSingleObject(
      ev,
      new WaitOrTimerCallback(WaitThreadFunc),
      4,
      2000,
      false//表示每次完成等待操作后都重置计时器,直到注销等待
      );
ThreadPool.QueueUserWorkItem (new WaitCallback (ThreadFunc),8);
Thread.Sleep (10000);
   return 0;
}
    public static void ThreadFunc(object b)
{ Console.WriteLine ("the object is {0}",b);
for(int i=0;i<2;i++)
{ Thread.Sleep (1000);
   ev.Set();
}
}
public static void WaitThreadFunc(object b,bool t)
{ Console.WriteLine ("the object is {0},t is {1}",b,t);
    }
其运行结果为: 
the object is 8
the object is 4,t is False
the object is 4,t is False
the object is 4,t is True
the object is 4,t is True
the object is 4,t is True
      从以上结果我们可以看出线程ThreadFunc运行了1次,而WaitThreadFunc运行了5次。我们可以从WaitOrTimerCallback中的bool t参数判断启动此线程的原因:t为false,则表示由于waitObject,否则则是由于超时。另外我们也可以通过object b向线程传递一些参数。

3、Timer
      它适用于需周期性调用的方法,它不在创建计时器的线程中运行,它在由系统自动分配的单独线程中运行。这和Win32中的SetTimer方法类似。它的构造为:
public Timer(
   TimerCallback callback,//所需调用的方法
   object state,//传递给callback的参数
   int dueTime,//多久后开始调用callback
   int period//调用此方法的时间间隔
); // 如果 dueTime 为0,则 callback 立即执行它的首次调用。如果 dueTime 为 Infinite,则 callback 不调用它的方法。计时器被禁用,但使用 Change 方法可以重新启用它。如果 period 为0或 Infinite,并且 dueTime 不为 Infinite,则 callback 调用它的方法一次。计时器的定期行为被禁用,但使用 Change 方法可以重新启用它。如果 period 为零 (0) 或 Infinite,并且 dueTime 不为 Infinite,则 callback 调用它的方法一次。计时器的定期行为被禁用,但使用 Change 方法可以重新启用它。
      在创建计时器之后若想改变它的period和dueTime,我们可以通过调用Timer的Change方法来改变:
public bool Change(
   int dueTime,
   int period
);//显然所改变的两个参数对应于Timer中的两参数
public static int   Main(string[] args)

Console.WriteLine ("period is 1000");
Timer tm=new Timer (new TimerCallback (TimerCall),3,1000,1000);
Thread.Sleep (2000);
Console.WriteLine ("period is 500");
tm.Change (0,800);
Thread.Sleep (3000);
return 0;
   }
public static void TimerCall(object b)

Console.WriteLine ("timercallback; b is {0}",b);
}
其运行结果为:
period is 1000
timercallback;b is 3 
timercallback;b is 3 
period is 500
timercallback;b is 3 
timercallback;b is 3 
timercallback;b is 3 
timercallback;b is 3 
                        
总结
      从以上的简单介绍,我们可以看出它们各自使用的场合:Thread适用于那些需对线程进行复杂控制的场合;ThreadPool适应于一些需要多个线程而又较短任务(如一些常处于阻塞状态的线程);Timer则适用于那些需周期性调用的方法。只要我们了解了它们的使用特点,我们就可以很好的选择合适的方法。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值