我很困惑如何使用POSIX使用Mutex。请考虑以下代码:
void *print_message_function( void *ptr );
pthread_mutex_t count_mutex = PTHREAD_MUTEX_INITIALIZER;
main()
{
pthread_t thread1, thread2,thread3;
std::string message1 = "Apple";
std::string message2 = "Orange";
int iret1, iret2,iret3;
iret1 = pthread_create( &thread1, NULL, print_message_function, (void*) &message1);
iret2 = pthread_create( &thread2, NULL, print_message_function, (void*) &message1);
iret3 = pthread_create( &thread3, NULL, print_message_function, (void*) &message2);
pthread_join( thread1, NULL);
pthread_join( thread2, NULL);
pthread_join( thread3, NULL);
exit(EXIT_SUCCESS);
}
void *print_message_function( void *ptr )
{
pthread_mutex_lock( &count_mutex );
int i = 3 ;
while( i ) {
printf("i is %d ...%s \n", i,(*(std::string *)ptr).c_str() );
i-- ;
sleep (1) ;
}
pthread_mutex_unlock( &count_mutex );
}
Thread1和线程2使用公共资源 - message1。 Thread3使用自己的资源 - 消息3。 搞乱STDOUT打印对我来说没问题。
程序的输出是
i is 3 ...Apple
i is 2 ...Apple
i is 1 ...Apple
i is 3 ...Apple
i is 2 ...Apple
i is 1 ...Apple
i is 3 ...Orange
i is 2 ...Orange
i is 1 ...Orange
正如我们看到thread3在最后执行。由于thread3不使用任何公共资源,我怎样才能使它"跳过互斥锁"。如何仅在两个线程访问公共内存时才能启用互斥锁,否则不能。互斥锁必须是动态的。我甚至可以指定一个变量,它不应该指向内存中的相同位置以激活锁。 换句话说,我怎样才能制作一个仅在两个线程碰撞时才被激活的互斥锁。
我理解这可能是一个重复的问题。但我无法找到解决此问题的任何方法。此外,由于一些环境限制,我不能使用C ++ 11 STL线程或提升。我想在pthreads库中提供帮助。
答案 0 :(得分:2)
现在你的问题是你在同一个对象上做mutex_lock。这样的事情会起作用吗?
struct DataWithMutex {
std::string message;
pthread_mutex_t count_mutex;
};
void *print_message_function( void *ptr );
main()
{
pthread_t thread1, thread2,thread3;
DataWithMutex data1 = {"Apple", PTHREAD_MUTEX_INITIALIZER};
DataWithMutex data2 = {"Orange", PTHREAD_MUTEX_INITIALIZER};
int iret1, iret2,iret3;
iret1 = pthread_create( &thread1, NULL, print_message_function, (void*) &data1);
iret2 = pthread_create( &thread2, NULL, print_message_function, (void*) &data1);
iret3 = pthread_create( &thread3, NULL, print_message_function, (void*) &data2);
pthread_join( thread1, NULL);
pthread_join( thread2, NULL);
pthread_join( thread3, NULL);
exit(EXIT_SUCCESS);
}
void *print_message_function( void *ptr )
{
DataWithMutex* const data = (DataWithMutex*)ptr;
pthread_mutex_lock( &(data->count_mutex) );
int i = 3 ;
while( i ) {
printf("i is %d ...%s \n", i,data->message.c_str() );
i-- ;
sleep (1) ;
}
pthread_mutex_unlock( &(data->count_mutex) );
}
答案 1 :(得分:1)
我会说,如果您持有的互斥锁已经存在足够长的时间以致这是一个问题,那么您的代码可能会出错。只需将互斥锁保持的时间减少到绝对最小值,并且在上面的代码示例中,问题将得到解决。
答案 2 :(得分:0)
你提到你不能使用C ++ 11,而你正在使用std::string
,所以我假设你可以使用C ++ 03。
一个解决方案是这样的:
(实时,可编辑和可运行的示例:http://coliru.stacked-crooked.com/a/376cfc4b50405333)
#include <iostream>
#include <pthread.h>
#include <unistd.h>
class LockGuard {
public:
LockGuard(pthread_mutex_t& mutex)
: m(mutex)
{
pthread_mutex_lock(&m);
}
~LockGuard()
{
pthread_mutex_unlock(&m);
}
private:
pthread_mutex_t& m;
};
class Access {
public:
Access(std::string& message)
: msg(message) {}
virtual std::string read() const
{
return msg;
}
virtual void write(const std::string& new_msg)
{
msg = new_msg;
}
protected:
std::string& msg;
};
class LockedAccess : public Access {
public:
LockedAccess(std::string& message)
: Access(message)
{
pthread_mutex_init(&mutex, NULL);
}
std::string read() const
{
LockGuard lock(mutex);
return msg;
}
void write(const std::string& new_msg)
{
LockGuard lock(mutex);
msg = new_msg;
}
private:
mutable pthread_mutex_t mutex;
};
void* print_message_function(void* ptr)
{
Access* accessor = static_cast<Access*>(ptr);
int i = 3;
while (i)
{
printf("i is %d ... %s \n", i, accessor->read().c_str());
i--;
sleep(1);
}
return NULL;
}
int main()
{
std::string message1 = "Apple";
std::string message2 = "Orange";
Access unlocked_access(message2);
LockedAccess locked_access(message1);
pthread_t thread1, thread2, thread3;
int iret1, iret2, iret3;
iret1 = pthread_create(&thread1, NULL, print_message_function, static_cast<void*>(&locked_access));
iret2 = pthread_create(&thread2, NULL, print_message_function, static_cast<void*>(&locked_access));
iret3 = pthread_create(&thread3, NULL, print_message_function, static_cast<void*>(&unlocked_access));
pthread_join(thread1, NULL);
pthread_join(thread2, NULL);
pthread_join(thread3, NULL);
return 0;
}
在这里,我们利用:
RAII(或基于范围的资源管理)以确保锁定正确,并在函数退出时进行解锁,即使这是通过异常
虚拟发送根据我们提供的类型执行不同的操作。
这个想法是提供一个用于访问消息的类接口,并提供一个派生类,它提供相同的接口,但在引擎盖下做了不同的事情 - 它需要锁定。您的线程函数现在可以继续调用函数read(),虚拟调度将确定是否调用了锁定版本或未锁定版本。
您仍然需要确定资源是否已共享,并将其包装在适当的Access类中。真的没有办法解决这个问题;一般来说,编译器和系统事先并不知道某个东西只能被一个线程访问。如果您无法提前做出决定,那么您可以做的最好的事情是为每个消息提供一个互斥锁,以便锁定是独立的(即线程3对于“Oranges”消息的锁定没有争用,但仍然会有锁定它;线程1和2将争夺“Apples”消息的锁定。
如果你实际上不能使用C ++,你可以通过锁定每条消息/一块潜在的共享数据来实现C语中大致类似的东西。您可以将数据和锁一起捆绑到一个结构中,并将指向该结构的指针传递给您的线程。然后线程必须在访问结构的其他成员之前获取锁。
作为最后一点,您应该尝试为您的特定程序确定合理的锁定模式;拿锁会很贵,特别是在争用的时候。通常引用锁粒度,它指的是锁覆盖了多少代码。在您的示例中,整个线程函数由锁覆盖。在上面的代码中,仅涵盖了对共享变量的访问。这允许竞争线程以更频繁地获取和释放锁的代价来取得更多进展。您需要的模式取决于您的应用程序,您可能需要花一些时间考虑最佳方法。