我希望以聪明的方式使用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);
我觉得这样效率很低,可以做得更好。你能给我一些想法吗?我试过用等待,但我不明白:/ 谢谢!
答案 0 :(得分:0)
我寻找好的和C ++方式,我强烈建议你阅读C++ Concurrency in Action: by Anthony Williams并留下pthread来尽可能地使用期货和类似的高级别的东西。如果你必须使用手动线程摆弄,你也可以找到很好的例子。
你的问题陈述对于明智的建议来说太模糊了 - 好的线程的基本思想是根本没有共享状态,并且对于你的握手情况很可能,使用为此目的而制作的一些同步队列。
答案 1 :(得分:0)
更智能的方法是使用std::mutex
和std::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::thread
或STTCL 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;
}