更新:我在下面的回答中提供了问题的原因及其解决方案。
我想实现多线程,它基于生产者 - 消费者方法进行图像处理任务。对于我的情况,library(httr)
set_config(config(ssl_verifypeer = 0L))
线程应该抓取图像并将它们放入Producer
,而消费者线程应该从container
线程中提取图像。我认为我应该使用Container
来实现queue
。
我想使用此SO answer中建议的以下代码。但我已经对container
的实现感到困惑,并将传入的图像放入container
线程中。
问题:第一个Producer
显示的图片不包含完整数据。并且,第二个consumer thread
从不显示任何图像。可能是,存在一些竞争情况或锁定情况,因为第二个线程根本无法访问队列数据。我已经尝试使用consumer thread
。
Mutex
答案 0 :(得分:1)
我在原始问题中没有看到实际问题,因此我将为您提供我在大学课程中实施生产者 - 消费者的参考资料。
http://cs360.byu.edu/static/lectures/winter-2014/semaphores.pdf
幻灯片13和17给出了生产者 - 消费者的好例子
我在实验室中使用了这个,我在这里发布了我的github: https://github.com/qzcx/Internet_Programming/tree/master/ThreadedMessageServer
如果你查看我的server.cc,你可以看到我对生产者 - 消费者模式的实现。
请记住,使用此模式,您无法切换等待语句的顺序,否则您可能会陷入死锁。
希望这有用。
编辑:
好的,这里是我上面链接的代码中的消费者 - 生产者模式的摘要。生产者消费者背后的想法是有一种线程安全的方式来传递来自"生产者"线程到"消费者"工人线程。在我的示例中,要完成的工作是处理客户端请求。生产者线程(.serve())监视传入的套接字并将连接传递给消费者线程(.handle())以在它们进入时处理实际的请求。此模式的所有代码都可以在server.cc中找到。 file(在server.h中有一些声明/导入)。
为了简短起见,我遗漏了一些细节。一定要浏览每一行并了解发生了什么。查看我正在使用的库函数以及参数的含义。我在这里给了你很多帮助,但是你仍然需要做很多工作才能获得充分的理解。
<强> PRODUCER:强>
就像我上面提到的,整个生产者线程都在.serve()函数中找到。它做了以下事情
在.serve()函数的底部,您可以看到以下代码:
while ((client = accept(server_,(struct sockaddr *)&client_addr,&clientlen)) > 0) {
sem_wait(clients_.e); //buffer check
sem_wait(clients_.s);
clients_.q->push(client);
sem_post(clients_.s);
sem_post(clients_.n); //produce
}
首先,检查缓冲区信号量&#34; e&#34;确保您的队列中有空间来放置请求。第二,获得信号量&#34; s&#34;为队列。然后将您的任务(在本例中为客户端连接)添加到队列中。释放队列的信号量。最后,使用信号量&#34; n&#34;向消费者发出信号。
<强>消费者:强>
在.handle()方法中,你真的只关心线程的开头。
while(1){
sem_wait(clients_.n); //consume
sem_wait(clients_.s);
client = clients_.q->front();
clients_.q->pop();
sem_post(clients_.s);
sem_post(clients_.e); //buffer free
//Handles the client requests until they disconnect.
}
消费者对生产者采取类似的行动,但反过来。首先,消费者等待生产者在信号量上发出信号&#34; n&#34;。请记住,因为有多个消费者,消费者可能最终获得此信号量是完全随机的。他们争夺它,但只有一个人可以通过这个信号量的每个sem_post移动。其次,他们像生产者那样获得队列信号量。弹出队列中的第一个项目并释放信号量。最后,他们在缓冲信号量上发出信号&#34; e&#34;缓冲区现在有更多空间。
<强>声明:强>
我知道信号量有可怕的名字。他们与我教授的幻灯片相匹配,因为那是我学习它的地方。我认为他们代表以下几点:
答案 1 :(得分:1)
好的,所以尽可能简单。您将需要2个线程,互斥锁,队列和2个线程处理函数。
Header.h
static DWORD WINAPI ThreadFunc_Prod(LPVOID lpParam);
static DWORD WINAPI ThreadFunc_Con(LPVOID lpParam);
HANDLE m_hThread[2];
queue<int> m_Q;
mutex m_M;
添加所有需要的东西,这些只是你需要的核心部分
Source.cpp
DWORD dwThreadId;
m_hThread[0] = CreateThread(NULL, 0, this->ThreadFunc_Prod, this, 0, &dwThreadId);
// same for 2nd thread
DWORD WINAPI Server::ThreadFunc_Prod(LPVOID lpParam)
{
cYourClass* o = (cYourClass*) lpParam;
int nData2Q = GetData(); // this is whatever you use to get your data
m_M.lock();
m_Q.push(nData2Q);
m_M.unlock();
}
DWORD WINAPI Server::ThreadFunc_Con(LPVOID lpParam)
{
cYourClass* o = (cYourClass*) lpParam;
int res;
m_M.lock();
if (m_Q.empty())
{
// bad, no data, escape or wait or whatever, don't block context
}
else
{
res = m_Q.front();
m_Q.pop();
}
m_M.unlock();
// do you magic with res here
}
在主要结束时 - 不要忘记使用WaitForMultipleObjects
所有可能的例子都可以直接在MSDN中找到,所以对此有很好的评论。
PART2:
好的,所以我相信标题是可自我解释的,所以我会给你更多的描述来源。你的源代码中的某个地方(甚至可以在构造函数中)你创建线程 - 如何创建线程的方式可能不同但想法是相同的(在win-线程在posix中创建之后运行,你必须加入)。我相信你将拥有一个可以启动你所有魔法的功能,让我们称之为MagicKicker()
如果是posix,请在构造函数中创建线程并在MagicKicker()
中加入em,win - 在MagicKicker()
中创建
你需要声明(在标题中)两个函数,你的线程函数将被实现ThreadFunc_Prod
和ThreadFunc_Prod
,这里重要的魔法就是你将对象的引用传递给这个函数( coz线程基本上是静态的)所以你可以轻松访问共享资源作为队列,互斥体等...
这些功能实际上正在开展工作。实际上,你需要在代码中使用它,只需在生产者中添加例程:
int nData2Q = GetData(); // this is whatever you use to get your data
m_M.lock(); // locks mutex so nobody cant enter mutex
m_Q.push(nData2Q); // puts data from producer to share queue
m_M.unlock(); // unlock mutex so u can access mutex in your consumer
并将此添加到您的消费者:
int res;
m_M.lock(); // locks mutex so u cant access anything wrapped by mutex in producer
if (m_Q.empty()) // check if there is something in queue
{
// nothing in you queue yet OR already
// skip this thread run, you can i.e. sleep for some time to build queue
Sleep(100);
continue; // in case of while wrap
return; // in case that u r running some framework with threadloop
}
else // there is actually something
{
res = m_Q.front(); // get oldest element of queue
m_Q.pop(); // delete this element from queue
}
m_M.unlock(); // unlock mutex so producer can add new items to queue
// do you magic with res here
答案 2 :(得分:0)
我的问题中提到的问题是Consumer thread
显示的图像不包含完整数据。 Consumer thread
显示的图片包含多个补丁,表明无法获取Producer thread
生成的完整数据。
答案背后的原因是Mat image
while loop
内的Consumer thread
声明。第二轮Mat
开始后,while loop
内创建的while loop
实例会被删除,因此Producer thread
永远无法访问创建的Mat image
数据在Consumer thread
。
解决方案:我应该这样做
struct ThreadSafeContainer
{
queue<Mat> safeContainer;
};
struct Producer
{
Producer(std::shared_ptr<ThreadSafeContainer> c) : container(c)
{
}
void run()
{
while(true)
{
// grab image from camera
// store image in container
Mat image(400, 400, CV_8UC3, Scalar(10, 100,180) );
mu.lock();
container->safeContainer.push(Mat);
mu.unlock();
}
}
std::shared_ptr<ThreadSafeContainer> container;
};
struct Consumer
{
Consumer(std::shared_ptr<ThreadSafeContainer> c) : container(c)
{
}
~Consumer()
{
}
void run()
{
while(true)
{
// read next image from container
mu.lock();
if (!container->safeContainer.empty())
{
Mat image= container->safeContainer.front(); //The front of the queue contain the image
container->safeContainer.pop();
imshow("consumer image", image);
waitKey(33);
}
mu.unlock();
}
}
std::shared_ptr<ThreadSafeContainer> container;
};