zoukankan      html  css  js  c++  java
  • [C#网络应用编程]2、对线程的管理

    在System.Threading命名空间下,有一个Thread类,用于对线程进行管理,如创建线程、启动线程、终止线程、合并线程、让线程休眠等

     Thread类 (假设Thread firTh = new 线程实例)

     firTh.IsBackground 返回一个bool值,判断或设置是否属于后台线程

    默认情况下,属于托管线程池的线程(即IsThreadPoolThread为true)都属于后台线程,而通过创建并启动新的Thread对象生成的都属于前台线程。

    如果使用一个线程监视某些活动(如Socket),可以将IsBackground设置为true,以遍该线程不会影响进程终止。

    1、启动线程

    Thread t1 = new Thread(方法名); //创建一个线程,该线程通过委托执行指定的方法,从.net2.0开始可以这么写,否则需要加ThreadStart

    如果该方法带有参数,可以在启动时传递:
    t1.Start(); //无参

    t1.Start("I'm param"); //有参数

    参数只能有一个,只能是object类型,如果希望传递多个,可以把所有参数封装到一个类,再传该类的实例。

    2、终止线程

    有两种方法,第一种是事先设置一个bool值,在其他线程修改这个bool值表示是否需要终止,在该线程中循环判断该布尔值,以确定是否需要退出该线程。

    第二种是调用Abort()方法,该方法的最终效果是强行终止该线程。但线程实际上不一定会立即结束,因为系统在结束线程前要进行代码清理工作,这需要一定的时间,因此在调用Abort()方法后,如果自动清理工作还未结束,可能会出现类似于死机的假死现象。为了解决这个问题,可以在主线程中调用Join()方法,并在Join方法中指定主线程等待子线程结束的等待时间。

    推荐第一种方法,实际工作中也是一般使用第一种方法。

    3、暂停线程

    Thread.Sleep(int 毫秒) 该方法为静态方法

    4、合并线程

    t1.Join(); 

    如果线程t1需要在线程t2执行结束才继续执行,可以在t1中调用t2.Join(),但是如果t2一直不执行完,那么t1也无法执行,因此可以给Join加一个参数:

    t1.Join(1000); 这样t1至多只会等待1000毫秒,然后无论t2是否结束,都继续往下执行。

    在一个线程中访问由另一个线程创建的控件

    直接访问会报异常:从不是创建控件的线程访问它。 现在有两种办法可以实现这个功能:

    第一种是使用委托和事件完成。

    第二种是利用BackroundWorker组件实现。

    1. 委托

    首先在类里面定义一个委托

    然后在需要用到的地方实例化委托

    通过控件的InvokeRequired判断该控件是否属于该线程,并使用委托或者直接调用需要的方法:

    public partial class Form1 : Form
     {
            delegate void ToAddSth(string item);  //在类中定义委托
    
            ...
    
            private void AddMessage(string item)
            {            
                ToAddSth myDele = new ToAddSth(AddMessage); //实例化委托
                if(TextBox1.InvokeRequired)   //根据控件的InvokeRequired判断是否需要委托
                {
                     TextBox1.Invoke(myDele,item);  //利用委托
                }
                else
                {
                     TextBox1.Items.Add(item);  //直接调用
                }
            }
     } 

     2.BackgroundWorker

    这个控件被实例化后,一般会用到其两个属性和三个方法:

    bw1.WorkerReportsProgress = true; //设置能否报告进度更新

    bw1.WorkerSupportsCancellation = true; //设置是否支持异步取消

    bw1.DoWork += SayHello; //异步执行的事件,可用bw1.RunWorkerAsync() 方法启动该事件

    bw1.ProgressChanged += ShowTime; //显示最新数据到窗体上的事件,可用bw1.ReportProgress(0, object) 方法调用该事件

    bw1.RunWorkerCompleted += GameOver; //停止DoWork事件时 发生的事件。用bw1.CancelAsync() 调用该事件

            BackgroundWorker backgroundWorker1;
            public Form1()
            {
                InitializeComponent();
                backgroundWorker1 = new BackgroundWorker();
                backgroundWorker1.WorkerReportsProgress = true;
                backgroundWorker1.WorkerSupportsCancellation = true;
                backgroundWorker1.DoWork += background1_DoWork;
                backgroundWorker1.ProgressChanged += background1_ProcessChanged;
                backgroundWorker1.RunWorkerCompleted += background1_RunWorkerCompleted;
                button2.Enabled = false;
            }
            private void button1_Click(object sender, EventArgs e)
            {
                richTextBox1.Text = "开始产生10000以内的随机数…
    ";
                button1.Enabled = false;
                button2.Enabled = true;
                backgroundWorker1.RunWorkerAsync();
            }
            private void button2_Click(object sender, EventArgs e)
            {
                backgroundWorker1.CancelAsync();
                button1.Enabled = true;
                button2.Enabled = false;
            }
            private void background1_DoWork(object sender, DoWorkEventArgs e)
            {
                Random r=new Random();
                BackgroundWorker bgw1 = sender as BackgroundWorker;
                int numCount = 0;
                while (!bgw1.CancellationPending)
                {
                    int num = r.Next(10000);
                    if (num % 5 == 0)
                    {
                        numCount++;
                    }
                    bgw1.ReportProgress(0, num);
                    Thread.Sleep(100);
                }
                e.Result = numCount;
            }
            private void background1_ProcessChanged(object sender, ProgressChangedEventArgs e)
            {
                int num = (int)e.UserState;
                richTextBox1.Text += num + " ";
            }
            private void background1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
            {
                if (e.Error == null)
                {
                    richTextBox1.Text += "
    操作停止,共产生 " + e.Result + " 个能被5整除的数!";
                }
                else
                {
                    richTextBox1.Text += "
    操作错误:" + e.Error;
                }
            }
    View Code

    volatile修饰符

    volatile修饰符表示所声明的字段可以被多个并发执行的线程修改。如果某个字段声明包含volatile,则该字段将不再被编译器所优化,这样确保它在任何时间呈现的都是最新值。

    对于多个线程访问的字段,而且该字段没有用lock语句对访问进行序列化,则应该用volatile进行声明。

    volatile修饰符只能包含在类或结构的声明中,不能将局部变量声明为volatile。

      class Class1
        {
            public bool shouldStop;//这里设置为volatile是关键,该关键字无法修饰属性
            private Form1 form1;
    
            public Class1(Form1 form)
            {
                this.form1 = form;
            }
    
            public void Method1(object obj)
            {
                string msg = obj as string;
                form1.AddMessage(msg);
                while (!shouldStop) //当开关打开一直循环
                {
                    Thread.Sleep(100);
                    form1.AddMessage("a");
                }
                form1.AddMessage("now a stoped!");//跳出循环
            }
            public void Method2()
            {
                while (!shouldStop)
                {
                    Thread.Sleep(100);
                    form1.AddMessage("b");
                }
                form1.AddMessage("now b stoped!");
            }
        }
    public partial class Form1 : Form
        {
            delegate void ToAddSth(string item);
            Class1 c1;
            Thread th1, th2;
    
            public Form1()
            {
                InitializeComponent();
                c1 = new Class1(this);
                button1.Click += new EventHandler(button1_Click);
                button2.Click += new EventHandler(button2_Click);
            }
            private void button1_Click(object sender, EventArgs e)
            {
                textBox1.Text = "";
                c1.shouldStop = false;
                th1 = new Thread(c1.Method1);
                th2 = new Thread(c1.Method2);
                th1.IsBackground = true;
                th2.IsBackground = true;
                th1.Start("a start");
                th2.Start();
            }
            private void button2_Click(object sender, EventArgs e)
            {
                c1.shouldStop = true;
                th1.Join(0);
                th2.Join(0);
            }
            public void AddMessage(string item)
            {
                ToAddSth myDele = new ToAddSth(AddMessage); //实例化委托
                if (textBox1.InvokeRequired)   //根据控件的InvokeRequired判断是否需要委托
                {
                    textBox1.Invoke(myDele, item);  //利用委托
                }
                else
                {
                    textBox1.AppendText(item + "
    ");  //直接调用
                    textBox1.ScrollToCaret(); //保持滚动条最底部
                }
            }
    
        }

     线程的优先级

    C#中线程共5个优先级,从高到低为:Highest、AboveNormal、Normal、BelowNormal、Lowest

    Thread th1 = new Thread(方法名);
    th1.priority = ThreadPriority.AboveNormal; //将线程th1的优先级设置为AboveNormal

    所设置的优先级仅仅适用于这些线程所属的进程。

    注意,当把某个线程设置为Highest时,系统上正在运行的其他线程都会终止,所以除非遇到马上需要处理的任务,否则不要轻易使用这个优先级。

    线程同步

    多线程解决了吞吐量和响应速度的问题,但是同时带来了共享资源的问题,如死锁和资源争用。

    多线程特别适合需要不同资源的任务(如果文件句柄和网络连接)。而为单个资源分配多线程的时候,线程可能会被频繁阻止以等待其他线程,从而与使用多线程的初衷背道而驰。

    所谓线程同步,是指多个线程之间存在先后执行顺序的关联关系,如果线程a必须在线程b工作完成后才能继续执行,则必须考虑如何让其保持同步,以确保不会出现死锁或者逻辑错误。

    System.Threading 命名空间提供了多个用于线程同步的类,包括Mutex、Monitor、Interlocked和AutoResetEvent。

    但在实际工作中可能用的最多的不是这些类,而是C#提供的lock语句。该语句简化了编程,使程序看起来清晰简洁。

    1. lock关键字

    lock关键字将代码段标记为临界区,它的实现原理是首先锁定某一个似有对象,然后执行代码段中的语句,当代码段中的语句执行完毕后,再接触该锁定。

    一般使用形式如下:

    private object obj = new object();

    ...

    lock(obj)

    {

      ...

    }

    注意,锁定的对象一般声明为object类型,并且一定是private,绝对不能是public,否则会导致lock语句无法控制。

    考虑这样一种情况:线程a将锁定的对象obj1声明为public,线程b将锁定的对象obj2声明为public,当a和b同时分别锁定obj1,obj2,由于都希望访问对方锁定的那个对象,并且在得到访问权之前都不会释放自己锁定的对象,从而产生死锁。

    欲锁定类的静态变量可以使用lock(typeof(ClassName))

    线程池(ThreadPool)

    线程池是后台执行多个任务的线程集合。一般服务端接使用线程池,为每个传入请求分配一个线程,从而达到异步处理请求的目的。

    线程池不会占用主线程,也不会延迟后续请求的处理。它有一个最大线程数限制,如果所有线程都繁忙,则额外的任务将放入队列中,直到有线程可用。

    一旦一项任务被加入到线程池的队列中,就不能取消该任务,直到该任务完成。

    所有线程池中的线程都是后台线程,这意味着当所有前台线程都推出后,ThreadPool线程也会退出。

    ThreadPool类常用的方法:

    GetAvailableTheads():检索由GetMaxThreads 返回的线程池线程的最大数目和当前活动数目之间的差值。 即可用的数量

    GetMaxThreads():检索可以同时活动的线程的数目。所有大于该数目的请求将保持队列状态,直到线程池线程变为可用。

    GetMinThreads():检索线程池在新请求预测中维护的空闲线程数

    QueueUserWorkItem():将方法插入队列以便执行。此方法在有线程池线程变得可用时执行  ThreadPool.QueueUserWorkItem(new WaitCallback(MethordName),object state(可选));

    SetMaxThreads():设置同时获得的线程的数目。

    SetMinThreads():设置线程池在新请求预测中维护的空闲线程数

    RegisterWaitForSingleObject():注册一个委托等待WaitHandle

    线程池适合需要多个线程而实际执行时间又不多的场合。如有些经常处于等待的线程。

    当服务器接收到大量而短小的请求时,线程池是非常合适的。它可以大大减少线程的创建和销毁次数,从而提升效率。

    如果线程运行的时间较长,此时运行时间比创建线程的时间长很多,此时线程池的作用就不那么明显,需要借助其他的技术来提高服务器的效率。

  • 相关阅读:
    poj 1113 Wall 凸包的应用
    NYOJ 78 圈水池 (入门级凸包)
    Monotone Chain Convex Hull(单调链凸包)
    poj Sudoku(数独) DFS
    poj 3009 Curling 2.0(dfs)
    poj 3083 Children of the Candy Corn
    Python join()方法
    通过FISH和下一代测序检测肺腺癌ALK基因融合比较
    华大病原微生物检测
    NGS检测ALK融合大起底--转载
  • 原文地址:https://www.cnblogs.com/dengshaojun/p/4146424.html
Copyright © 2011-2022 走看看