在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以内的随机数…\r\n";
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 = ;
while (!bgw1.CancellationPending)
{
int num = r.Next();
if (num % == )
{
numCount++;
}
bgw1.ReportProgress(, num);
Thread.Sleep();
}
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 += "\r\n操作停止,共产生 " + e.Result + " 个能被5整除的数!";
}
else
{
richTextBox1.Text += "\r\n操作错误:" + e.Error;
}
}

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();
form1.AddMessage("a");
}
form1.AddMessage("now a stoped!");//跳出循环
}
public void Method2()
{
while (!shouldStop)
{
Thread.Sleep();
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();
th2.Join();
}
public void AddMessage(string item)
{
ToAddSth myDele = new ToAddSth(AddMessage); //实例化委托
if (textBox1.InvokeRequired) //根据控件的InvokeRequired判断是否需要委托
{
textBox1.Invoke(myDele, item); //利用委托
}
else
{
textBox1.AppendText(item + "\r\n"); //直接调用
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

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

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

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

[C#网络应用编程]2、对线程的管理的更多相关文章

  1. Java并发编程(01):线程的创建方式,状态周期管理

    本文源码:GitHub·点这里 || GitEE·点这里 一.并发编程简介 1.基础概念 程序 与计算机系统操作有关的计算机程序.规程.规则,以及可能有的文件.文档及数据. 进程 进程是计算机中的程序 ...

  2. (转载)JAVA线程池管理

    平时的开发中线程是个少不了的东西,比如tomcat里的servlet就是线程,没有线程我们如何提供多用户访问呢?不过很多刚开始接触线程的开发攻城师却在这个上面吃了不少苦头.怎么做一套简便的线程开发模式 ...

  3. Java线程池管理及分布式Hadoop调度框架搭建

    平时的开发中线程是个少不了的东西,比如tomcat里的servlet就是线程,没有线程我们如何提供多用户访问呢?不过很多刚开始接触线程的开发工程师却在这个上面吃了不少苦头. 怎么做一套简便的线程开发模 ...

  4. Java并发编程扩展(线程通信、线程池)

    之前我说过,实现多线程的方式有4种,但是之前的文章中,我只介绍了两种,那么下面这两种,可以了解了解,不懂没关系. 之前的文章-->Java并发编程之多线程 使用ExecutorService.C ...

  5. (九) 一起学 Unix 环境高级编程 (APUE) 之 线程

    . . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编 ...

  6. (十) 一起学 Unix 环境高级编程 (APUE) 之 线程控制

    . . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编 ...

  7. Solr4.8.0源码分析(3)之index的线程池管理

    Solr4.8.0源码分析(3)之index的线程池管理 Solr建索引时候是有最大的线程数限制的,它由solrconfig.xml的<maxIndexingThreads>8</m ...

  8. Java并发编程:Java线程池

    转载自:http://www.cnblogs.com/dolphin0520/p/3932921.html 在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题 ...

  9. GPU编程自学5 —— 线程协作

    深度学习的兴起,使得多线程以及GPU编程逐渐成为算法工程师无法规避的问题.这里主要记录自己的GPU自学历程. 目录 <GPU编程自学1 -- 引言> <GPU编程自学2 -- CUD ...

随机推荐

  1. Android开发学习之路--Broadcast Receiver之初体验

    学习了Activity组件后,这里再学习下另一个组件Broadcast Receiver组件.这里学习下自定义的Broadcast Receiver.通过按键自己发送广播,然后自己接收广播.新建MyB ...

  2. Android开发学习之路--MAC下Android Studio开发环境搭建

    自从毕业开始到现在还没有系统地学习android应用的开发,之前一直都是做些底层的驱动,以及linux上的c开发.虽然写过几个简单的app,也对android4.0.3的源代码做过部分的分析,也算入门 ...

  3. Axure实现淡入淡出效果

    小伙伴们有可能在各大网站看到淡入淡出效果的动画,比如淘宝.京东,淘宝每天会把各种打折促销.今日推荐.限时抢购等做成淡入淡入或者向右活动等类似翻页的效果放在首页,吸引顾客的眼球,那么如何使用Axure来 ...

  4. Linux上程序调试的基石(2)--GDB

    3. GDB的实现 GDB是GNU发布的一个强大的程序调试工具,用以调试C/C++程序.可以使程序员在程序运行的时候观察程序在内存/寄存器中的使用情况.它的实现也是基于ptrace系统调用来完成的.  ...

  5. Hadoop:Hadoop基本命令

    http://blog.csdn.net/pipisorry/article/details/51223877 常用命令 启用hadoop start-dfs.sh start-hbase.sh 停止 ...

  6. Hessian探究(一)Hessian入门示例

    一.hessian的maven信息: [html] view plain copy print? <dependency> <groupId>com.caucho</gr ...

  7. SDL2源代码分析2:窗口(SDL_Window)

    ===================================================== SDL源代码分析系列文章列表: SDL2源代码分析1:初始化(SDL_Init()) SDL ...

  8. 【Android 应用开发】 Android 相关代码规范 更新中 ...

    . 简介 : Android 常用的代码结构, 包括包的规范, 测试用例规范, 数据库模块常用编写规范; 参考 : 之前写的一篇博客 [Android 应用开发] Application 使用分析 ; ...

  9. boost::bad_weak_ptr的原因

    出现boost::bad_weak_ptr最可能的原因是enable_shared_from_this<>类构造函数中调用shared_from_this(), 因为构造尚未完成,实例还没 ...

  10. redis3.0.5在linux上安装与配置

    redis3.0.5在linux上安装与配置 rhel6/ubuntu14 1 下载 # wget http://download.redis.io/releases/redis-3.0.5.tar. ...