仅在线程中发生冲突时使用互斥锁

时间:2014-07-29 08:14:32

标签: c multithreading pthreads mutex

我很困惑如何使用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库中提供帮助。

3 个答案:

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

说明:

在这里,我们利用:

  1. RAII(或基于范围的资源管理)以确保锁定正确,并在函数退出时进行解锁,即使这是通过异常

  2. 虚拟发送根据我们提供的类型执行不同的操作。

  3. 这个想法是提供一个用于访问消息的类接口,并提供一个派生类,它提供相同的接口,但在引擎盖下做了不同的事情 - 它需要锁定。您的线程函数现在可以继续调用函数read(),虚拟调度将确定是否调用了锁定版本或未锁定版本。

    您仍然需要确定资源是否已共享,并将其包装在适当的Access类中。真的没有办法解决这个问题;一般来说,编译器和系统事先并不知道某个东西只能被一个线程访问。如果您无法提前做出决定,那么您可以做的最好的事情是为每个消息提供一个互斥锁,以便锁定是独立的(即线程3对于“Oranges”消息的锁定没有争用,但仍然会有锁定它;线程1和2将争夺“Apples”消息的锁定。

    在C:

    如果你实际上不能使用C ++,你可以通过锁定每条消息/一块潜在的共享数据来实现C语中大致类似的东西。您可以将数据和锁一起捆绑到一个结构中,并将指向该结构的指针传递给您的线程。然后线程必须在访问结构的其他成员之前获取锁。

    锁定粒度:

    作为最后一点,您应该尝试为您的特定程序确定合理的锁定模式;拿锁会很贵,特别是在争用的时候。通常引用锁粒度,它指的是锁覆盖了多少代码。在您的示例中,整个线程函数由锁覆盖。在上面的代码中,仅涵盖了对共享变量的访问。这允许竞争线程以更频繁地获取和释放锁的代价来取得更多进展。您需要的模式取决于您的应用程序,您可能需要花一些时间考虑最佳方法。