re: C#线程系列讲座(4):同步与死锁 留恋星空 2008-07-26 08:58
多看看这个,以后的面试题都是小Case
re: C#线程系列讲座(4):同步与死锁 雅阁布 2008-07-26 08:36
不错!!
re: C#线程系列讲座(4):同步与死锁 jiudian 2008-07-26 03:28
强呵.............
re: C#线程系列讲座(4):同步与死锁 Tony Zhou 2008-07-26 01:41
private Object lockN = new Object();
lock(lockN )
和
lock(this)
效果上面有什么区别?
死锁和线程池的大小关系不大,而是Handler是否被另一只关联等待的线程互相等待。前面有人已经说了。。。
这是我说的。两个线程互相等待。还举了例子,而另外一个人举了另外一个死锁的例子,就是一个线程里调用另一个线程时的死锁。
我的文件服务器是多线程,不是单线程地, 只是线程之间没有关联,如是不会死锁,这好象没错吧。
至于死锁和线程池的关系,有,但不大。它们之间的关系我也是听@Angel Lucifer说的。 主要是线程池会限制线程运行的个数,所以会出现 Angel Lucifer描述的这种情况。
--引用--------------------------------------------------
本文提供的文件下载服务器是绝对不会死锁发生的,顶多由于线程过多,把下载任务给扔了。
--------------------------------------------------------
不知道您老为什么突然这么说,对于单线程的东西会死锁?呵呵。
死锁和线程池的大小关系不大,而是Handler是否被另一只关联等待的线程互相等待。前面有人已经说了。。。
没错,需要设成 ThreadPool.SetMaxThreads(2, 2);methodc才无法调用。
但你的这种产生死锁的方式是通过在线程里启动另外一个线程而造成的,而且在线程中运行另外一个线程,还必须要调用join或EndXXX,必须要满足这两个条件死锁才会发生。 而且这种死锁也非常好查找,但只要不这样做,也就是不在一个线程里运行另一个线程,就算是运行另外一个线程,也不去调用EndXXX或join,这样则可完全避免这种方式的死锁了,最闹心的就是因同步而产生的死锁。这种死锁方式是防不胜防啊。
本文提供的文件下载服务器是绝对不会死锁发生的,顶多由于线程过多,把下载任务给扔了。
re: C#线程系列讲座(3):线程池和文件下载服务器 Angel Lucifer 2008-07-19 13:16
忘记你是双核的了,呵呵。
ThreadPool.SetMaxThreads 不能设置最大线程数小于计算机的处理器数目。因为设置不成功,所以还是默认的每CPU 250。
双核情况下,可以考虑设置成 2 或者以上,只要它能返回 true 。
代码可以考虑使用你自己的嵌套三层或者 N 层。
PS : 在实际的异步编程中,可能会大量使用 ThreadPool.QueueUserWorkItem 或者异步委托(它基于线程池实现)。如果不注意,很快就会超过 25 导致死锁。我们很难避免这种情况不发生。这正是线程池中线程数量从 25 提升到 250 的原因。
再次PS: 锁机制相当麻烦,一不小心,就会翻船。.NET ThreadPool 的实现也不是十分高效。
能解释这是为什么吗?感觉ThreadPool.SetMaxThreads(1, 1);没起作用。
但这种死种方式只是原因之一,而更复杂的死锁是在运行线程之间的死锁。
再加一层也没有死:
public static void Main()
{
ThreadPool.SetMaxThreads(1, 1);
WaitCallback callback = delegate { MethodA(); };
callback.BeginInvoke(null, null, null);
Console.ReadLine();
}
private static void MethodA()
{
Console.WriteLine("Thread A is beginning");
WaitCallback callback = delegate { MethodB(); };
IAsyncResult result = callback.BeginInvoke(null, null, null);
//模拟实际工作。
Console.WriteLine("1");
Console.WriteLine("2");
Console.WriteLine("3");
//这里永远也不会返回。
callback.EndInvoke(result);
}
private static void MethodB()
{
//永远也不会运行到这里,因为线程池线程耗尽,导致资源竞争引发了死锁。
Console.WriteLine("Thread A is beginning");
WaitCallback callback = delegate { MethodC(); };
IAsyncResult result = callback.BeginInvoke(null, null, null);
//模拟实际工作。
Console.WriteLine("1");
Console.WriteLine("2");
Console.WriteLine("3");
//这里永远也不会返回。
callback.EndInvoke(result);
}
private static void MethodC()
{
Console.WriteLine("methodc");
}
@Angel Lucifer
不好意思,你的这段代码会死锁呢?我在机器上调试,并没有死锁啊,
这种用方式是由于没有线程执行任务导致的死锁,但不知怎么着,在我的机器上没死,会不会是因为双核的缘故。
re: C#线程系列讲座(3):线程池和文件下载服务器 Angel Lucifer 2008-07-19 10:35
本来写了一些解释,结果 Firefox 下老出问题,搞的俺灰心了。
琢磨一下吧,:-)
re: C#线程系列讲座(3):线程池和文件下载服务器 Angel Lucifer 2008-07-19 10:32
using System.Threading;
namespace Lucifer.CSharp.Sample


{
class Program

{
public static void Main()

{
ThreadPool.SetMaxThreads(1, 1);


WaitCallback callback = delegate
{ MethodA(); };
callback.BeginInvoke(null, null, null);

Console.ReadLine();
}

private static void MethodA()

{
Console.WriteLine("Thread A is beginning
");


WaitCallback callback = delegate
{ MethodB(); };
IAsyncResult result = callback.BeginInvoke(null, null, null);

//模拟实际工作。
Console.WriteLine("1
");
Console.WriteLine("2
");
Console.WriteLine("3
");

//这里永远也不会返回。
callback.EndInvoke(result);
}

private static void MethodB()

{
//永远也不会运行到这里,因为线程池线程耗尽,导致资源竞争引发了死锁。
Console.WriteLine("Thread B is beginning
");
}
}
}
re: C#线程系列讲座(3):线程池和文件下载服务器 Angel Lucifer 2008-07-19 10:09
@银河使者
文字描述太麻烦,用一段代码来说明吧。
只有可能发生死锁的任务在不同的线程中同时运行,就可能会发生死锁。 如果这些线程不同时运行,当然就会不发生死锁。
但不知将25增到250可以降低死锁发生的概率是从何说起,怎么将可同时运行的线程数增大就会降低死锁的概率呢?请@ Angel Lucifer 明示
re: C#线程系列讲座(3):线程池和文件下载服务器 Angel Lucifer 2008-07-18 22:28
避免死锁主要还是开发人员的责任,因为线程池并不知道开发人员如何同步。
此外,如果使用 Lock-Free ,则根本不会有死锁的可能。
re: C#线程系列讲座(3):线程池和文件下载服务器 Angel Lucifer 2008-07-18 22:26
@银河使者
我当然清楚死锁的概念。.NET 3.5 之所以从 25 提高到 250的目的是为了降低发生死锁的概率,而不是说它一定会发生死锁,呵呵。
如果下载到501个文件时,页前500个都没下载完,服务器会处理等待状态,在客户端会看到浏览器的进度条正在前进,但不会出现下载对话框,直到有其他的自由线程。或者由于超时而被server抛弃这个任务。
如果有100000个人同时下载文件,那会出现什么情况?从第501个开始服务器没有响应,是这样吗?
re: C#线程系列讲座(3):线程池和文件下载服务器 airwolf2026 2008-07-18 21:12
呵呵.还行.
所谓死锁是两个或多个线程互相等待造成的,并不是线程少了就容易死锁,多了就不死了。 还有死锁也是不无法分配线程造成的,那也不叫死锁,所果在线程池中分配不到线程,这个任务就会被放到等待队列中,直接有空闲thread来处理它,或者在超时时间后被丢弃。
死锁其实就是两个或多个线程互相被锁住了。如下面代码(只是伪代码,大概意识)如示:
thread1
{
lock(a)
{
// 假设thread1刚好执行到这被thread2把cpu夺走了
lock(b) { ... }
}
}
thread2
{
lock(b)
{
// 假设thread2刚好执行到这被thread1把cpu夺走了
lock(a) { ... }
}
}
我们可以想象,如果thread1和thread2正好执行到了上面代码注释的部分。那么thread1中要执行lock(b),而thread2中要执行lock(a),但这时a和b都被锁住了,所有就死了。 当然,解决这个问题的方法也非常简单,就是thread1和thread2的lock语句顺序保持一致,代码如下:
thread1
{
lock(a)
{
lock(b) { ... }
}
}
thread2
{
lock(a)
{
lock(b) { ... }
}
}
上面代码是永远不会发生死锁地。
re: C#线程系列讲座(3):线程池和文件下载服务器 Angel Lucifer 2008-07-18 20:33
仅部分同意,这个问题有相关的说明,线程数并非越多越好,呵呵。
当线程池有太多线程等待其他任务结束时就会出现死锁:一旦所有25个线程都被阻塞的时候,等待中的任务就无法分配到线程了。250只是降低了这种死锁的可能。
我想增大工作线程数主要是为了在默认情况下可同时处理更多的工作。至于死锁,是要用同步解决的,和线程池中的线程个数无关。再说ThreadPool最然是500个工作线程,但也是一开始一个一个起的(虽然可以设置),不是一起都运行时。就算是50000个线程也同样会发生dead lock。 因为死锁是相关的一些线程之间协作不好而造成的,和其他无关的线程没有任何关系。
re: C#线程系列讲座(3):线程池和文件下载服务器 Angel Lucifer 2008-07-18 20:09
/*
* 在默认情况下,ThreadPool最大可建立500个工作线程和1000个I/O线程
*/
在 .NET 2.0 中, ThreadPool 中默认最大工作线程是每 CPU 25个。
在 .NET 3.5 中, ThreadPool 中默认最大工作线程是每 CPU 250个。
这个变化是为了处理线程池中的死锁,但无法彻底排除死锁的可能性,只是让该问题的发生概率大大减小而已。
由此,可以推知楼主的机器是双核 CPU,安装了 .NET Framework 3.5 或者 .NET Framework 2.0 SP1,:-)。
当调用一个线程的join方法时,如果这个线程未执行完,程序将被阻塞。
不错,用Join来等待所有线程完成工作的办法很神奇~
确定多个线程是否完成的方法很多,但基本可以分为主动式和被动式。主动式就是由每个线程在结束时来通知主线程或其他监视线程。如本文的第一种方法就属性主动式,当然可以将write(...)改成回调函数形式了。
被动方式是由其他的监视线程对多个线程进行监控,本文的第二种方法就属性这种方式。
受教育了
多个线程,一般是弄个异步代理,然后写个回调方法 来通知主线程,线程结束!
是不是我blog右边加的那个flash时钟的原因啊,我把它去了看看
--引用--------------------------------------------------
XeonWell: 改天再看,打开该文章好几次,cpu都长期在94%以上,不知道楼主用了啥. 虽然很喜欢楼主的文章,但是在这炎热的夏天,cpu要紧.
--------------------------------------------------------
确实是这样的,不过文章也确实是好!
re: C#线程系列讲座(2):Thread类的应用 GuoYong.Che 2008-07-17 08:40
真不错,学习了
问个问题,是不是一些异步操作会用到IOCP功能?MSDN好像没有说哪个XXXBegin...用IOCP吧?另外那些WorkerItem???这样的工作线程和普通thread有啥区别?嘿嘿,要是楼主后续的文章有这些就更好咯.
是的,直接使用EndInvoke方法,主线程会被阻塞。其实EndInvoke方法相当于Thread.join方法,这个join方法的最大用处就是可以确保多个线程同时结束,再往下执行,EndInvoke的作用和join类似,如果有多个委托使用了BeginInvoke方法,那么可以用这些委托的EndInvoke方法来确保它们都执行完后,再往下进行。如果没这个需求,就要通过回调来使用EndInvoke方法了
疑问 wangxm 2008-07-14 16:17
同上疑问,除了回调不会阻塞主线程,前面几种异步委托的有实际意义?
文章写的不错..
有个疑问:
以第一种方法来说,主线程应该是阻塞的吧?
呵呵,既然是异步,就不要让主线程阻塞.
确定所有线程是否都完成了工作的方法有很多,如可以采用类似于对象计数器的方法,所谓对象计数器,就是一个对象被引用一次,这个计数器就加1,销毁引用就减1,如果引用数为0,则垃圾搜集器就会对这些引用数为0的对象进行回收。
方法一:线程计数器
线程也可以采用计数器的方法,即为所有需要监视的线程设一个线程计数器,每开始一个线程,在线程的执行方法中为这个计数器加1,如果某个线程结束(在线程执行方法的最后为这个计数器减1),为这个计数器减1。 然后再开始一个线程,按着一定的时间间隔来监视这个计数器,如是棕个计数器为0,说明所有的线程都结束了。当然,也可以不用这个监视线程,而在每一个工作线程的最后(在为计数器减1的代码的后面)来监视这个计数器,也就是说,每一个工作线程在退出之前,还要负责检测这个计数器。
使用这种方法不要忘了同步这个计数器变量啊,否则会产生意想不到的后果。
方法二:使用Thread.join方法
join方法只有在线程结束时才继续执行下面的语句。可以对每一个线程调用它的join方法,但要注意,这个调用要在另一个线程里,而不要在主线程,否则程序会被阻塞的。
个人感觉这种方法比较好。
我在下一讲会详细描述这两种方法的具体实现过程。
上述两种方法都没有线程数的限制(当然,仍然会受到操作系统和硬件资源的限制)
哪位读者有更好的方法也可以跟贴!!
借个地方问个多线程的问题。
工作中经常要多线程测试一些业务逻辑,在编写测试程序的时候,如何确定所有的线程都完成了“工作”?
曾经试过用WaitAll,但是有数量限制,具体数字忘记了,反正20线程以下肯定没问题,但有时候测试需要创建50+,甚至100+的线程去测试,WaitAll就无能为力了。
目前的办法是测试项目(console模式)最后用一个ReadKey()来等待~