基于生产者 - 消费者的多线程图像处理

时间:2015-07-08 13:31:15

标签: c++ multithreading opencv image-processing computer-vision

更新:我在下面的回答中提供了问题的原因及其解决方案。

我想实现多线程,它基于生产者 - 消费者方法进行图像处理任务。对于我的情况,library(httr) set_config(config(ssl_verifypeer = 0L)) 线程应该抓取图像并将它们放入Producer,而消费者线程应该从container线程中提取图像。我认为我应该使用Container来实现queue

我想使用此SO answer中建议的以下代码。但我已经对container实现感到困惑,并将传入的图像放入container线程中。

问题:第一个Producer显示的图片不包含完整数据。并且,第二个consumer thread从不显示任何图像。可能是,存在一些竞争情况或锁定情况,因为第二个线程根本无法访问队列数据。我已经尝试使用consumer thread

Mutex

3 个答案:

答案 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()函数中找到。它做了以下事情

  • 初始化信号量。由于操作系统的不同,这里有两个版本。我在OS X上编程,但必须在Linux上输入代码。由于信号量与操作系统绑定,因此了解如何在特定设置中使用信号量非常重要。
  • 设置客户端与之交谈的套接字。对您的申请不重要。
  • 创建消费者线程。
  • 监视客户端套接字并使用生产者模式将项目传递给使用者。此代码如下

在.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;缓冲区现在有更多空间。

<强>声明:

我知道信号量有可怕的名字。他们与我教授的幻灯片相匹配,因为那是我学习它的地方。我认为他们代表以下几点:

  • e for empty:如果生成器已满,则此信号量会阻止生产者推送队列中的更多项目。
  • s for semaphore:我最不喜欢的。但我教授的风格是为每个共享数据结构都有一个结构。在这种情况下&#34;客户_&#34;是包含所有三个信号量和队列的结构。基本上,这个信号量是为了确保没有两个线程同时触及相同的数据结构。
  • n表示队列中的项目数。

答案 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_ProdThreadFunc_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;
};