同时为队列中的对象执行函数时出现奇怪的行为

strange behavior in concurrently executing a function for objects in queue

本文关键字:对象 队列 执行 函数      更新时间:2023-10-16

我的程序有一个共享queue,主要分为两部分:

一个用于将类request的实例推送到queue另一个用于访问queue中的多个request对象并处理这些对象。 request是一个非常简单的类(仅用于测试),具有string req字段。

我正在研究第二部分,这样做,我想保留一个scheduling thread,以及多个(在我的示例中,两个)executing threads

我想有一个单独的scheduling thread的原因是减少lock的数量,并通过多个executing threads unlock访问queue的操作。

我正在使用 pthread 库,我的调度和执行函数如下所示:

void * sched(void* elem) {
    queue<request> *qr = static_cast<queue<request>*>(elem);
    pthread_t pt1, pt2;
    if(pthread_mutex_lock(&mut) == 0) {
        if(!qr->empty()) {
            int result1 = pthread_create(&pt1, NULL, execQueue, &(qr->front()));
            if (result1 != 0) cout << "error sched1" << endl;
qr->pop();
        }
        if(!qr->empty()) {
            int result2 = pthread_create(&pt2, NULL, execQueue, &(qr->front()));
            if (result2 != 0) cout << "error sched2" << endl;
qr->pop();
        }
        pthread_join(pt1, NULL);
        pthread_join(pt2, NULL);
        pthread_mutex_unlock(&mut);
    }
    return 0;
}
void * execQueue(void* elem) {
    request *r = static_cast<request*>(elem);
    cout << "req is: " << r->req << endl; // req is a string field
    return 0;
}

简单地说,execQueue每个线程都有一个要执行的线程,并且只是输出通过void* elem参数传递给它的请求。

schedmain() 中调用,带有一个线程,(如果您想知道如何调用,它会在main()中调用,如下所示)

pthread_t schedpt;
int schresult = pthread_create(&schedpt, NULL, sched, &q);
if (schresult != 0) cout << "error sch" << endl;
pthread_join(schedpt, NULL);

并且 sched 函数本身创建多个(此处为两个)executing threads并从queue pop s request s,并通过在多个线程上调用 execQueue(pthread_create然后ptrhead_join)来执行 request s。

问题是程序的奇怪行为。

当我检查队列中的大小和元素而不创建线程并在多个线程上调用它们时,它们正是我所期望的。

但是,当我使用多个线程运行程序时,它会打印出来

队列中有 1 个项目。队列中有 2 个项目。要求是:要求是:第一!(x' j|1 rj|p rj|1 FIRST! ' j|!' j| ' j|P ( ( (1 i|p i|

最后一行不断变化。

所需的输出为

队列中有 1 个项目。队列中有 2 个项目。要求是:第一要求是:第一

我想要么我在多个线程上调用execQueue的方式,要么我pop()的方式是错误的,但我无法找出问题所在,也找不到任何来源来参考正确的用法。

请帮我解决这个问题。请耐心等待我笨拙地使用 pthread,因为我是初学者。

您的队列包含对象,而不是指向对象的指针。您可以按原样通过operator &()对队列前面的对象进行寻址,但是一旦弹出队列,该对象就消失了,该地址不再有效。当然,sched不在乎,但您发送该地址的execQueue功能肯定会乎。

对代码最直接的修复是:

更改此设置:

pthread_create(&pt1, NULL, execQueue, &(qr->front()));

对此:

// send a dynamic *copy* of the front queue node to the thread
pthread_create(&pt1, NULL, execQueue, new request(qr->front()));

并且您的线程进程应更改为:

void * execQueue(void* elem) 
{
    request *r = static_cast<request*>(elem);
    cout << "req is: " << r->req << endl; // req is a string field
    delete r;
    return nullptr;
}

也就是说,我可以想到更好的方法来做到这一点,但这应该解决你的直接问题,假设你的request对象类是可复制构造的,如果它有动态成员,则遵循三法则。

这是

您温和消毒的c ++ 11版本,因为我需要一个简单的测试来安装MSVC2013:)

科里鲁现场观看

#include <iostream>
#include <thread>
#include <future>
#include <mutex>
#include <queue>
#include <string>
struct request { std::string req; };
std::queue<request> q;
std::mutex queue_mutex;
void execQueue(request r) {
    std::cout << "req is: " << r.req << std::endl; // req is a string field
}
bool sched(std::queue<request>& qr) {
    std::thread pt1, pt2;
    {
        std::lock_guard<std::mutex> lk(queue_mutex);
        if (!qr.empty()) {
            pt1 = std::thread(&execQueue, std::move(qr.front()));
            qr.pop();
        }
        if (!qr.empty()) {
            pt2 = std::thread(&execQueue, std::move(qr.front()));
            qr.pop();
        }
    }
    if (pt1.joinable()) pt1.join();
    if (pt2.joinable()) pt2.join();
    return true;
}
int main()
{
    auto fut = std::async(sched, std::ref(q));
    if (!fut.get()) 
        std::cout << "error" << std::endl;
}

当然,它现在实际上并没有做太多事情(因为队列中没有任务)。