• C#多线程编程


    NET将关于多线程的功能定义在System.Threading名字空间中。因此,要使用多线程,必须先声明引用此名字空间(using System.Threading;)。
    1)启动线程

    启动线程就是创建一个新的线程,如下面代码:

    Thread thread1 = new Thread(new ThreadStart(Count));
    thread1.Start();

    其中Count是将要执行的线程

    2)杀死线程

    杀死线程就是将一线程斩草除根,为了不白费力气,在杀死线程之前最好判断一下此线程是否还是活着(用IsAlive属性),然后通过Abort()方法杀死线程

    3)暂停线程

    它的意思是让一个正在运行的线程休眠一段时间,如thread1.Sleep(1000)让线程停止一秒

    4)优先级

    Thread中有ThreadPriority属性,它是来设置优先级的,但是不能保证系统接收此优先级,一个线程的优先级可分为5种:Normal,AboveNormal,BelowNormal,Highest,Lowest;具体例子如下:

    thread1.Priority = ThreadPriority.Highest;

    5)线程挂起
    Thread类的Suspend是用来挂起线程的,直到调用Resume,线程才可以继续执行,如果线程已经挂起那就不起作用了,代码如下:

    if(thread1.State == ThreadState.Running)
    {
        thread1.Suspend();
    }

    6)恢复线程
    Thread类的Resume是用来恢复线程的,如果线程已经启动,那么改方法不起作用,代码如下:

    if(thread1.State == ThreadState.Suspend)
    {
        thread1.Resume();
    }

    下面一个例子,简单的体现了下多线程处理功能

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading; //引入线程命名空间
    
    namespace ThreadExample
    {
        class Program
        {
            public static void ThreadProc()
            {
                for (int i = 0; i < 10; i++)
                {
                    Console.WriteLine("ThreadProc:{0}", i);
                    Thread.Sleep(1000);
                }
            }
            static void Main(string[] args)
            {
                Console.WriteLine("主线程:开始运行第二个线程");
                Thread thread1 = new Thread(new ThreadStart(ThreadProc));
                thread1.Start();
                for (int i = 0; i < 4; i++)
                {
                    Console.WriteLine("主线程:Do Some Work");
                    Thread.Sleep(1000);
                }
                Console.WriteLine("主线程:使用Join方法,直到ThreadProc结束");
                thread1.Join();
                Console.WriteLine("主线程:ThreadProc.Join has return,按任意键结束");
                Console.ReadLine();
            }
        }
    }

    运行结果如下:

    在Visual C#中的System.Threading命名空间提供了许多使得可以多线程编程的类和接口,其中线程的创建有3种方法,分别是Thread、ThreadPool、Timer;

    一、Thread

    这也许是线程最复杂的方法,但它提供了对线程的灵活控制,首先你必须使用它的构造函数创建一个线程实例,它的参数比较简单,只有一个ThreadStart委托:Public Thread(ThreadStart start);然后调用Start()启动它,当然你可以利用它的Proiority来设置或获得它的优先级(enum ThreadPirority:Normal,AboveNormal,BelowNormal,Highest,Lowest).

    下面设置两个线程实例t1和t2,分别设置它的优先级,然后启动线程(两线程基本一样,只不过它们输出不一样,t1为“1”,t2为“2”,根据它们各自输出字符个数比可大致看出它们占用CPU时间之比,这也反映出了它们各自的优先级)

    static void Main(string[] args)
    {
       Thread t1 = new Thread(new ThreadStart(Thread1));
       Thread t2 = new Thread(new ThreadStart(Thread2));
    
       t1.Priority = ThreadPriority.BelowNormal ;
       t2.Priority = ThreadPriority.Lowest ;
               t1.Start();
          t2.Start();
       }
    public static void Thread1()
    { 
       for (int i = 1; i < 1000; i++) 
       {//每运行一个循环就写一个“1”
         dosth();
        Console.Write("1");
       }
       }
    public static void Thread2()
    { 
       for (int i = 0; i < 1000; i++) 
       {//每运行一个循环就写一个“2”
        dosth();
        Console.Write("2");
       }
    }
    public static void dosth()
    {//用来模拟复杂运算
       for (int j = 0; j < 10000000; j++) 
       {    
        int a=15;
        a = a*a*a*a;
       }
    }

    以上程序运行结果为:
    11111111111111111111111111111111111111111121111111111111111111111111111111111111111112
    11111111111111111111111111111111111111111121111111111111111111111111111111111111111112
    11111111111111111111111111111111111111111121111111111111111111111111111111111111111112

    从以上结果我们可以看出,t1线程所占用CPU的时间远比t2的多,这是因为t1的优先级比t2的高,若我们把t1和t2的优先级都设为Normal,结果见下图:
    121211221212121212121212121212121212121212121212121212121212121212121
    212121212121212121212121212121212121212121212121212121212121212121212
    121212121212121212

    二、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则适用于那些需周期性调用的方法。只要我们了解了它们的使用特点,我们就可以很好的选择合适的方法。

  • 相关阅读:
    check2
    LYF模板连接.txt
    mvc中的表现和数据分离怎么理解?
    node中websocket的使用
    vue随笔
    python安装Django常见错误
    node中的session的使用
    为什么很多IT公司不喜欢进过培训机构的人呢
    vue数据交互
    vuecli的服务代理
  • 原文地址:https://www.cnblogs.com/study-programmer/p/3582351.html
Copyright © 2020-2023  润新知