Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 什么';我的生产者消费者队列设计有什么问题?_C#_Multithreading_Queue_Producer Consumer - Fatal编程技术网

C# 什么';我的生产者消费者队列设计有什么问题?

C# 什么';我的生产者消费者队列设计有什么问题?,c#,multithreading,queue,producer-consumer,C#,Multithreading,Queue,Producer Consumer,我从C代码示例开始。我尝试对其进行调整有两个原因:1)在我的场景中,所有任务都将在消费者开始之前放在前面的队列中,2)我想将工作者抽象到一个单独的类中,而不是在WorkerQueue类中有原始Thread成员 然而,我的队列似乎没有自行处理,它只是挂起,当我进入Visual Studio时,它被卡在WorkerThread的第Join()行上。还有,有没有更好的方法来组织这件事?关于公开WaitOne()和Join()方法的某些内容似乎是错误的,但是我想不出一种合适的方法来让WorkerThre

我从C代码示例开始。我尝试对其进行调整有两个原因:1)在我的场景中,所有任务都将在消费者开始之前放在前面的队列中,2)我想将工作者抽象到一个单独的类中,而不是在
WorkerQueue
类中有原始
Thread
成员

然而,我的队列似乎没有自行处理,它只是挂起,当我进入Visual Studio时,它被卡在
WorkerThread的
第Join()行上。还有,有没有更好的方法来组织这件事?关于公开
WaitOne()
Join()
方法的某些内容似乎是错误的,但是我想不出一种合适的方法来让
WorkerThread
与队列交互

另外,如果我使用
块在
顶部调用
q.Start(#)
,则每次启动时只有一些线程(例如线程1、2和8处理每个任务)。为什么会这样?这是某种比赛条件,还是我做错了什么

using System;
using System.Collections.Generic;
using System.Text;
using System.Messaging;
using System.Threading;
using System.Linq;

namespace QueueTest
{
    class Program
    {
        static void Main(string[] args)
        {
            using (WorkQueue q = new WorkQueue())
            {
                q.Finished += new Action(delegate { Console.WriteLine("All jobs finished"); });

                Random r = new Random();
                foreach (int i in Enumerable.Range(1, 10))
                    q.Enqueue(r.Next(100, 500));

                Console.WriteLine("All jobs queued");
                q.Start(8);
            }
        }
    }

    class WorkQueue : IDisposable
    {
        private Queue<int> _jobs = new Queue<int>();
        private int _job_count;
        private EventWaitHandle _wh = new AutoResetEvent(false);
        private object _lock = new object();
        private List<WorkerThread> _th;
        public event Action Finished;

        public WorkQueue()
        {
        }

        public void Start(int num_threads)
        {
            _job_count = _jobs.Count;
            _th = new List<WorkerThread>(num_threads);
            foreach (int i in Enumerable.Range(1, num_threads))
            {
                _th.Add(new WorkerThread(i, this));
                _th[_th.Count - 1].JobFinished += new Action<int>(WorkQueue_JobFinished);
            }
        }

        void WorkQueue_JobFinished(int obj)
        {
            lock (_lock)
            {
                _job_count--;
                if (_job_count == 0 && Finished != null)
                    Finished();
            }
        }

        public void Enqueue(int job)
        {
            lock (_lock)
                _jobs.Enqueue(job);

            _wh.Set();
        }

        public void Dispose()
        {
            Enqueue(Int32.MinValue);
            _th.ForEach(th => th.Join());
            _wh.Close();
        }

        public int GetNextJob()
        {
            lock (_lock)
            {
                if (_jobs.Count > 0)
                    return _jobs.Dequeue();
                else
                    return Int32.MinValue;
            }
        }

        public void WaitOne()
        {
            _wh.WaitOne();
        }
    }

    class WorkerThread
    {
        private Thread _th;
        private WorkQueue _q;
        private int _i;

        public event Action<int> JobFinished;

        public WorkerThread(int i, WorkQueue q)
        {
            _i = i;
            _q = q;
            _th = new Thread(DoWork);
            _th.Start();
        }

        public void Join()
        {
            _th.Join();
        }

        private void DoWork()
        {
            while (true)
            {
                int job = _q.GetNextJob();
                if (job != Int32.MinValue)
                {
                    Console.WriteLine("Thread {0} Got job {1}", _i, job);
                    Thread.Sleep(job * 10); // in reality would to actual work here
                    if (JobFinished != null)
                        JobFinished(job);
                }
                else
                {
                    Console.WriteLine("Thread {0} no job available", _i);
                    _q.WaitOne();
                }
            }
        }
    }
}
使用系统;
使用System.Collections.Generic;
使用系统文本;
使用系统消息传递;
使用系统线程;
使用System.Linq;
命名空间队列测试
{
班级计划
{
静态void Main(字符串[]参数)
{
使用(WorkQueue q=newworkqueue())
{
q、 Finished+=新操作(委托{Console.WriteLine(“所有作业已完成”);});
随机r=新随机();
foreach(可枚举范围(1,10)中的int i)
q、 排队(r.Next(100500));
Console.WriteLine(“排队的所有作业”);
q、 启动(8);
}
}
}
类工作队列:IDisposable
{
专用队列_作业=新队列();
私人内部工作计数;
private event waithandle _wh=新的自动存储事件(false);
私有对象_lock=新对象();
私人名单;;
公共事件行动完成;
公共工作队列()
{
}
公共无效开始(int num_线程)
{
_job_count=_jobs.count;
_th=新列表(num_线程);
foreach(可枚举范围中的int i(1,num_线程))
{
_添加(新WorkerThread(i,this));
_th[_th.Count-1].JobFinished+=新操作(工作队列_JobFinished);
}
}
无效工作队列\u作业已完成(int obj)
{
锁
{
_工作计数--;
if(_job_count==0&&Finished!=null)
完成的();
}
}
公共无效排队(int作业)
{
锁
_作业。排队(作业);
_wh.Set();
}
公共空间处置()
{
排队(Int32.MinValue);
_th.ForEach(th=>th.Join());
_wh.Close();
}
public int GetNextJob()
{
锁
{
如果(_jobs.Count>0)
return_jobs.Dequeue();
其他的
返回Int32.MinValue;
}
}
公营机构
{
_wh.WaitOne();
}
}
类WorkerThread
{
私有线程;
专用工作队列;
私人互联网;;
公共事件行动完成;
公共WorkerThread(inti,WorkQueue q)
{
_i=i;
_q=q;
_th=新螺纹(定位销);
_th.Start();
}
公共void Join()
{
_th.Join();
}
私房
{
while(true)
{
int job=_q.GetNextJob();
if(作业!=Int32.MinValue)
{
WriteLine(“线程{0}得到了作业{1}”,_i,作业);
Thread.Sleep(job*10);//事实上,这里的实际工作
如果(作业已完成!=null)
作业完成(作业);
}
其他的
{
WriteLine(“线程{0}没有可用的作业”,_i);
_q、 WaitOne();
}
}
}
}
}

工作线程都在DoWork()中的_q.WaitOne()调用上阻塞。调用线程的Join()方法将死锁,线程永远不会退出。您需要添加一个机制来向工作线程发出退出的信号。在工人中使用WaitAny测试的ManualReset事件将完成工作


调试提示:熟悉调试+窗口+线程窗口。它允许您在线程之间切换并查看它们的调用堆栈。您自己很快就会发现这个问题。

工作线程都阻塞了DoWork()中的_q.WaitOne()调用。调用线程的Join()方法将死锁,线程永远不会退出。您需要添加一个机制来向工作线程发出退出的信号。在工人中使用WaitAny测试的ManualReset事件将完成工作

调试提示:熟悉调试+窗口+线程窗口。它允许您在线程之间切换并查看它们的调用堆栈。您自己很快就会发现这个问题。

您在
DoWork
结束时执行了
WaitOne()
,但在线程开始运行后您从未设置它。
请注意,
AutoResetEvent
在“成功”
WaitOne

之后将返回到未设置状态。您在
DoWork
结束时执行
WaitOne()
,但在线程开始运行后从未设置它。

请注意,
AutoResetEvent
将在“成功”后返回到未设置状态
WaitOne

您的DoWork方法中的循环永远不会完成。这将导致线程始终处于繁忙状态,并且此线程.Join()将永远阻塞,w
    private void DoWork()
    {
        bool done = false;
        while (!done)
        {
            int job = _q.GetNextJob();
            if (job != Int32.MinValue)
            {
                Console.WriteLine("Thread {0} Got job {1}", _i, job);
                Thread.Sleep(job * 10); // in reality would to actual work here
                if (JobFinished != null)
                    JobFinished(job);
            }
            else
            {
                Console.WriteLine("Thread {0} no job available", _i);
                done = true;
            }
        }
    }