如何使用pthreads以智能方式将变量共享到线程?

时间:2013-05-28 17:00:12

标签: c++ multithreading pthreads

我希望以聪明的方式使用pthreads在C ++中同步线程。

我有一个全局变量:

int Resources = 0;

我有两个线程函数:

void *incResources(void *arg)
{
   while(1)
   {
     pthread_mutex_lock (&resourcesMutex);
     Resources += 2;
     pthread_mutex_unlock (&resourcesMutex);
   }

pthread_exit((void*) 0);
}

void *consumeResources(void *arg)
{
   while(1)
   {
     pthread_mutex_lock (&resourcesMutex);
     Resources--;
     pthread_mutex_unlock (&resourcesMutex);
   }
   pthread_exit((void*) 0);
 }

在main函数中,我初始化了两个消耗线程和一个递增线程:

pthread_mutex_init(&resourcesMutex, NULL);

pthread_create(&callThd[0], &attr, incResources, (void *)i);
pthread_create(&callThd[1], &attr, consumeResources, (void *)i);
pthread_create(&callThd[2], &attr, consumeResources, (void *)i);

我觉得这样效率很低,可以做得更好。你能给我一些想法吗?我试过用等待,但我不明白:/ 谢谢!

4 个答案:

答案 0 :(得分:0)

我寻找好的和C ++方式,我强烈建议你阅读C++ Concurrency in Action: by Anthony Williams并留下pthread来尽可能地使用期货和类似的高级别的东西。如果你必须使用手动线程摆弄,你也可以找到很好的例子。

你的问题陈述对于明智的建议来说太模糊了 - 好的线程的基本思想是根本没有共享状态,并且对于你的握手情况很可能,使用为此目的而制作的一些同步队列。

答案 1 :(得分:0)

更智能的方法是使用std::mutexstd::thread(或Boost等效项),这样您就不需要手动解锁互斥锁了。

条件变量将允许消费者阻止(不浪费CPU周期),直到有可用的工作:

struct Resource
{
  int value;
  std::mutex mx;
  std::condition_variable cv;
};

void incResources(Resource& res)
{
   while(1)
   {
     {
       std::lock_guard<std::mutex> l(res.mx);
       res.value += 2;
     }
     res.cv.notify_all();
   }
}

void consumeResources(Resource& res)
{
   while(1)
   {
     std::unique_lock<std::mutex> l(res.mx);
     while (res.value == 0)
       res.cv.wait(l);
     res.value--;
   }
}

并在主线程中:

Resources res;
res.value = 0;
std::thread t1(incResources, std::ref(res));
std::thread t2(consumeResources, std::ref(res));
std::thread t3(consumeResources, std::ref(res));
// ...
t1.join();
t2.join();
t3.join();

答案 2 :(得分:0)

我认为如果您使用的是C ++,则没有理由比C ++ 11 std::thread和STL同步类更喜欢本地使用pthread。

如果你不能使用C ++ 11标准,你应该将pthreads本机接口包装到合理的C ++类表示中(参见例如boost::threadSTTCL Posix Thread实现)。

答案 3 :(得分:0)

看起来你正在尝试实现一个生产者和消费者,+ =线程创建工作(数字要减少),消费者将它们带走。

不要让消费者处于这样的微不足的旋转循环中,而是看一下条件变量。

std::queue<Job*> queue;
pthread_mutex mutex;
pthread_cond cond;

void AddJob(Job* job) {
    pthread_mutex_lock(&mutex);
    queue.push_back(job);
    pthread_cond_signal(&cond);
    pthread_mutex_unlock(&mutex);
}

void* QueueWorker(void* /*threadInfo*/) {
    Job* job = NULL;
    for (;;) {
        pthread_mutex_lock(&mutex);
        while ( queue.empty() ) {
            // unlock the mutex until the cond is signal()d or broadcast() to.
            // if this call succeeds, we will have the mutex locked again on the other side.
            pthread_cond_wait(&cond, &mutex);
        }
        // take the first task and then release the lock.
        job = queue.pop();
        pthread_mutex_unlock(&mutex);

        if ( job != NULL )
           job->Execute();
    }

    return NULL;
}

这可以扩展到多个消费者。

顺便说一下,虽然熟悉pthreads实现可能很有用,但您应该查看其中一个可用的线程包装器。 C ++ 11引入了std :: thread和std :: mutex,很多人都对boost发誓,但我个人认为OpenSceneGraph团队的“OpenThreads”库是最简单,最优雅的库之一。

编辑:这是一个完整的工作实现,尽管有一种用于结束运行的有点人为的机制。

#include <queue>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

static int jobNo = 0;
class Job {
public:
    Job() : m_i(++jobNo) { printf("Created job %d.\n", m_i); }
    int m_i;
    void Execute() { printf("Job %d executing.\n", m_i); usleep(500 * 1000); }
};

std::queue<Job*> queue;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

void AddJob(Job* job) {
    pthread_mutex_lock(&mutex);
    queue.push(job);
    pthread_cond_signal(&cond);
    pthread_mutex_unlock(&mutex);
}

void* QueueWorker(void* /*threadInfo*/) {
    Job* job = NULL;
    for (;;) {
        pthread_mutex_lock(&mutex);
        while ( queue.empty() ) {
            // unlock the mutex until the cond is signal()d or broadcast() to.
            // if this call succeeds, we will have the mutex locked again on the other side.
            pthread_cond_wait(&cond, &mutex);
        }
        // take the first task and then release the lock.
        job = queue.front();
        queue.pop();
        pthread_mutex_unlock(&mutex);

        if ( job == NULL ) {
            // in this demonstration, NULL ends the run, so forward to any other threads.
            AddJob(NULL);
            break;
        }
        job->Execute();
        delete job;
    }
    return NULL;
}

int main(int argc, const char* argv[]) {
    pthread_t worker1, worker2;
    pthread_create(&worker1, NULL, &QueueWorker, NULL);
    pthread_create(&worker2, NULL, &QueueWorker, NULL);

    srand(time(NULL));

    // queue 5 jobs with delays.
    for ( size_t i = 0; i < 5; ++i ) {
        long delay = (rand() % 800) * 1000;
        printf("Producer sleeping %fs\n", (float)delay / (1000*1000));
        usleep(delay);
        Job* job = new Job();
        AddJob(job);
    }
    // 5 more without delays.
    for ( size_t i = 0; i < 5; ++i ) {
        AddJob(new Job);
    }
    // null to end the run.
    AddJob(NULL);

    printf("Done with jobs.\n");
    pthread_join(worker1, NULL);
    pthread_join(worker2, NULL);

    return 0;
}