您的位置:首页 > 编程语言 > C#

C# 线程池ThreadPool的学习

2016-10-08 14:09 337 查看
线程池的必要性:
官方解释:Manyapplications create threads that spend a great deal
of time in the sleepingstate, waiting for an event to occur.
Otherthreads might enter a sleeping state only to be awakened periodically to pollfor a change or update status information.

许多应用程序创建大量花费的时间处于睡眠状态,等待事件发生的线程。 其他线程可能会进入休眠状态,只是为了定期唤醒以轮询更改或更新的状态信息。 线程池,使您可以通过提供您的应用程序提供的由系统管理的工作线程池来更有效地使用线程。
在管理线程方面,线程池更为有效,因为这样可以减少创建和销毁线程的开销。 这意味着它可以跨多个 CPU 核心优化线程,并且它可以在应用之间及在后台任务运行时平衡线程资源。 使用内置线程池是一种方便的方法,因为你只需将注意力集中到编写完成任务的代码上,而不是线程管理机制上。

笔者总结:
1.线程池减少了创建,开始,停止,休眠的次数,提高了整个效率。
2.使用线程池,可以让我们把注意力集中在业务逻辑上,而不是复杂的线程管理
3.但是:如果需要前台线程,或者设置优先级(让两个线程的执行有频率差别),就需要自己创建线程。
就是说,自定义创建的多个线程,其中几个执行过一段代码之后,可能需要睡上一觉再被唤醒然后执行工作,那么你睡觉也是需要浪费空间滴。或者在销毁与创建之间也消耗CPU。线程池,就是你用我就给,不用我就销毁。随用随给,不用不给。 (请读者勘误。。)
来自 <https://msdn.microsoft.com/zh-cn/library/system.threading.threadpool.aspx#Y0>

线程池是后台线程:
Thethreads in the managed thread pool are background threads.
That is, their IsBackground properties
aretrue.
This means that a ThreadPool thread
will not keep an application runningafter all foreground threads have exited.

来自 <https://msdn.microsoft.com/zh-cn/library/system.threading.threadpool.aspx#Y0>

在托管的线程池线程是后台线程。 也就是说,其 IsBackground 属性是 true。 这意味着, ThreadPool 线程并不能保证所有前台线程均都退出之后运行的应用程序。

线程池的理解:
正如Pool,这是CLR已经开启好的线程,等待用户直接使用,线程池最多能开启1023个线程,1000个I/O线程。
开启方式:ThreadPool.QueueUserWorkItem(newWaitCallback(callPrint), i);
每执行一句就等于开启一个新线程,执行方式为异步,并发。
一段自己测试的代码:实现线程池的基本用法
using System.Text;
usingSystem.Threading.Tasks;
usingSystem.Threading;
namespaceThreadPoolText//线程池的学习,线程池开启的线程总是后台线程,跟随主程序结束而结束
{
class Program
{
static void Main(string[] args)
{
ThreadPool.SetMaxThreads(4,4);
for (int i = 0; i < 10; i++)
{
ThreadPool.QueueUserWorkItem(newWaitCallback(callPrint), i);//开启线程池
}//开启10个异步线程
//ThreadPool.GetMaxThreads()
Thread.Sleep(5000);//主程序等待线程池的程序结束
Console.WriteLine("主线程ID:"+Thread.CurrentThread.ManagedThreadId);
//Console.ReadKey();
}
static void callPrint(object Rx_date)
{
for (int i = 0; i < 20; i++)
{
Console.WriteLine("线程池执行的第" + Rx_date.ToString() + "次,输出结果:"+(i+1));
//Console.WriteLine(i);
}
Console.WriteLine("当前线程ID:"+Thread.CurrentThread.ManagedThreadId+"-------------------------------------------------------接下来换次");
}
}
}
线程池注意事项;
1.线程池开启的线程全部是后台线程,不能修改
2.线程池适合执行时间较短的函数来开启线程,适合小任务
3.线程池的线程开启后,执行都是异步的,也就是在起点时间的细微差别之后,几乎是同时执行的。假如主程序执行下面2句
{
ThreadPool.QueueUserWorkItem(newWaitCallback(callPrint), i);//开启线程池
01 第一句ThreadPool.QueueUserWorkItem(newWaitCallback(callPrint), i);//开启线程池
02第二句
}
主线程(这两个线程所在的函数)把这两个线程开启的时间差比如是25毫秒,第一句代理委托的函数执行却需要10毫秒,也就是说,第二句还没开启,第一句就执行完了,让人误以为线程池是同步按顺序执行的。所以应该延长各个要被执行的代码的执行时间,比如在第一句代理委托的函数的函数中,加上Thread.sleep(1000),你会发现,第一句还没有执行完毕的时候,第二句也开始执行,就是和第一句一起走在路上了。不过第一句先走了一步(25毫秒)
关于线程池的问题:
1.他们可以执行同一段代码,那么是异步(Asyn)还是同步(syn)?
实验结果:是异步的,也就是同时执行的,即便是访问同一资源,也是并发的,不需要谁等待谁。所以线程池不能解决同步的去访问同一资源问题。和普通线程一样。需要自己用
AutoResetEvent或者lock来解决,访问同一资源问题。
测试代码:
using System;
usingSystem.Collections.Generic;
using System.Linq;
using System.Text;
usingSystem.Threading.Tasks;
usingSystem.Threading;

namespaceThreadpoolandLock
{
class Program
{
static void Main(string[] args)
{
Account A_publiccode = newAccount(10000);
ThreadPool.SetMaxThreads(4, 4);
for (int i = 0; i < 4; i++)
{
ThreadPool.QueueUserWorkItem(A_publiccode.DoTransactions);//从线程池中拿4个线程用,四个刷卡器刷你的银行卡
}
Thread.Sleep(3000);//主线程等待子线程3秒钟。
Console.WriteLine("主线程结束!主线程ID:"
+ Thread.CurrentThread.ManagedThreadId);
}
}
class Account//你的银行账户
{
private Object thisLock = new Object();
int balance;//默认是私有的,通过构造函数来赋值的
Random r = new Random();
public Account(int initial)//构造函数
{
balance = initial;
}

private int Withdraw(int amount)//此函数是私有的,使用lock请避免锁定public对象
{
#region
if (balance < 0)
{
throw newException("Negative Balance");
}
#endregion
lock (thisLock)//锁定,一次只允许一个线程访问
{
if (balance >= amount)
{
Console.WriteLine("余额充足");
Console.WriteLine("本次余额: " + balance + "元");
Console.WriteLine("本次支出: -" + amount + "元");
balance = balance - amount;
Console.WriteLine("消费之后的余额 : " + balance + "元");
Thread.Sleep(1000);//拖延单个线程的时间,好证明线程池是异步并发的,在没有lock的情况下,揭露它真面目
Console.WriteLine("-------" +Thread.CurrentThread.ManagedThreadId);
return amount;
}
else { return 0; }
}
}
public void DoTransactions(objectdate)//随机花费的总额
{
for (int i = 0; i < 1; i++)
{
Withdraw(r.Next(1, 100));
}
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: