惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

S
Secure Thoughts
Security Latest
Security Latest
Simon Willison's Weblog
Simon Willison's Weblog
O
OpenAI News
GbyAI
GbyAI
L
LINUX DO - 最新话题
A
Arctic Wolf
T
Tor Project blog
G
GRAHAM CLULEY
I
InfoQ
博客园_首页
IT之家
IT之家
The Register - Security
The Register - Security
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
P
Proofpoint News Feed
The GitHub Blog
The GitHub Blog
Blog — PlanetScale
Blog — PlanetScale
N
Netflix TechBlog - Medium
K
Kaspersky official blog
博客园 - 三生石上(FineUI控件)
S
SegmentFault 最新的问题
U
Unit 42
PCI Perspectives
PCI Perspectives
量子位
P
Palo Alto Networks Blog
S
Securelist
T
Troy Hunt's Blog
博客园 - 【当耐特】
Recorded Future
Recorded Future
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
S
Security Affairs
Engineering at Meta
Engineering at Meta
T
The Blog of Author Tim Ferriss
博客园 - 聂微东
罗磊的独立博客
N
News and Events Feed by Topic
人人都是产品经理
人人都是产品经理
B
Blog RSS Feed
NISL@THU
NISL@THU
C
Cisco Blogs
T
Threatpost
有赞技术团队
有赞技术团队
Forbes - Security
Forbes - Security
Hugging Face - Blog
Hugging Face - Blog
Last Week in AI
Last Week in AI
T
The Exploit Database - CXSecurity.com
Cloudbric
Cloudbric
Cyberwarzone
Cyberwarzone
Google DeepMind News
Google DeepMind News
C
Cyber Attacks, Cyber Crime and Cyber Security

博客园 - o0myself0o

利用线程池实现多客户端和单服务器端Socket通讯(二):异步编程模型实现 利用线程池实现多客户端和单服务器端Socket通讯(一):同步方式 Entity Framework 4.0 ObjectContext下的各种方法实践 题目:若干个不重复数,打乱顺序输出 wtf js(四) - o0myself0o - 博客园 wtf js(三) number的类型不是number wtf js(二) 算法:给定两个已从小到大排好序的整型数组arrA和arrB,将两个数组合并成arrC,使得arrC也要按从小到大的顺序排好序 应用中的单例模式 面试题:给你三个bool类型变量a, b, c,判断至少有两个为true javascript面向对象编程(一) - o0myself0o - 博客园 wtf js(一) - o0myself0o - 博客园 社区网站功能实现系列(三):社区页面无刷新回发的一种实现方式 社区网站功能实现系列(二):社区内容分享到别的SNS 社区网站功能实现系列(一):多国语言的实现 反射获取Class中Property的值 A*寻路初探 闲谈ASP.NET 2.0缓存技术 使用 jQuery 简化 Ajax 开发
生产者消费者模式,代码中碰到的疑问(已解决)
o0myself0o · 2011-04-01 · via 博客园 - o0myself0o

代码如下:

namespace ProduceConsume
{
   
class Program
   {
       
static void Main(string[] args)
       {
           
int result = 0;
           Product product 
= new Product();
 
           
Producer producer = new Producer { Product = product, Looper = 10 };
           
Consumer consumer = new Consumer { Product = product, Looper = 10 };
 
           Thread threadP 
= new Thread(new ThreadStart(producer.Process));
           Thread.Sleep(
500);
           Thread threadC1 
= new Thread(new ThreadStart(consumer.Process));
           Thread.Sleep(
500);
           Thread threadC2 
= new Thread(new ThreadStart(consumer.Process));
 
           
try
           {
               threadP.Start();
               threadC1.Start();
               threadC2.Start();
 
               threadP.Join();
               threadC1.Join();
               threadC2.Join();
 
               Console.ReadLine();
           }
           
catch (ThreadStateException ex)
           {
               Console.WriteLine(
"ThreadStateException: " + ex.Message);
               result 
= 1;
           }
           
catch (ThreadInterruptedException ex)
           {
               Console.WriteLine(
"ThreadInterruptedException: " + ex.Message);
               result 
= 1;
           }
 
           Environment.ExitCode 
= result;
       }
   }
 
   
class Product
   {
       
bool consumeFlag = false;
       Queue
<int> queue = new Queue<int>();
 
       
public void Consume()
       {
           Monitor.Enter(
this);
           
if (!consumeFlag)
           {
               
try
               {
                   Monitor.Wait(
this);
               }
               
catch (Exception ex)
               {
                   Console.WriteLine(
"Exception: " + ex.Message);
               }
           }
 
           
if (queue.Count > 0)
           {
               Console.WriteLine(
"Consume --> Thread ID: {0}, Product Index: {1}, Product Left: {2}", Thread.CurrentThread.ManagedThreadId, queue.Dequeue(), queue.Count);
               consumeFlag 
= queue.Count > 0;
           }
 
           Monitor.Pulse(
this);
           Monitor.Exit(
this);
       }
 
       
public void Produce()
       {
           Monitor.Enter(
this);
           
if (consumeFlag)
           {
               
try
               {
                   Monitor.Wait(
this);
               }
               
catch (Exception ex)
               {
                   Console.WriteLine(
"Exception: " + ex.Message);
               }
           }
           
if (queue.Count == 0)
           {
               Random random 
= new Random();
               
int count = random.Next(10);
               
for (int i = 1; i <= count; i++)
               {
                   queue.Enqueue(i);
               }
               Console.Write(
"Produce --> ");
               Console.WriteLine(count 
== 1 ? "{0} product is enqueued." : "{0} products are enqueued.", count);
           }
           consumeFlag 
= true;
           Monitor.Pulse(
this);
           Monitor.Exit(
this);
       }
   }
 
   
class Producer
   {
       
public Product Product { getset; }
 
       
public int Looper { getset; }
 
       
public void Process()
       {
           
for (int i = 0; i < Looper; i++)
           {
               Product.Produce();
           }
       }
   }
 
   
class Consumer
   {
       
public Product Product { getset; }
 
       
public int Looper { getset; }
 
       
public void Process()
       {
           
for (int i = 0; i < Looper; i++)
           {
               Product.Consume();
           }
       }
   }
}

程序所做的事情是:

1. 生产者生产random.Next(10)个数放入队列中;

2. 消费者(threadC1, threadC2 两个线程,两个消费者)在队列不为空的情况下,做Dequeue()操作,直到队列为空;

3. 重复执行1步骤10次

开始没注意到代码中标注颜色区块的两个Looper次数,所以对下图中的结果很不理解,以为哪个地方死锁了

发现这个失误之后,将消费者的Looper设置为10*max(random.Next(10))=100,得到下图中的结果:

可以看出,每次生产的产品都可以被消费完(queue.Count==0),但是生产的次数没有达到设定的次数(10次)

仔细想过之后,才发现这跟Monitor.Pulse(obj)方法的作用有关。

Monitor背后管理着两个队列,这两个队列的元素都是线程,一个被称作就绪队列(ready),一个被称作等待队列(waiting), ready队列中存放的是准备获取锁的线程

ready队列中保存的是准备获取锁的线程。就是说,如果某个线程(记作线程A)执行了Monitor.Wait(),那么ready队列中队头的线程就会获得锁,开始运行(如果ready队列中没有线程,那么就没有这个效果);同时线程A自动进入waiting队列中的队尾了。

waiting队列中保存的是正在等待锁定对象状态变化的通知的线程。就是说,如果某个线程执行了Monitor.Pulse(),那么waiting队列中队头的线程就进入ready队列中。

在此程序中,当消费者消费完一个产品后,如果队列中还有产品,在调用Monitor.Pulse(this)的时候,对头的线程不一定是消费者线程,如果是生产者的线程,这就会花去一次loop,所以没有执行够10次生产是很正常的。