【问题标题】:pThread Mutex Locking without Global Mutex没有全局互斥锁的 pThread 互斥锁
【发布时间】:2012-06-05 09:06:53
【问题描述】:

我看到的所有使用 pThread 库进行互斥锁的教程都使用了全局互斥锁:

见:

https://computing.llnl.gov/tutorials/pthreads/#Mutexes

http://www.drdobbs.com/cpp/184401518?pgno=3(用于 boost::thread 但相同的上下文)

我想做的是在主文件的全局超出需要锁定变量的函数的范围时使用互斥锁。这是一个例子:

Main.cpp

int main() {
    some initilisation code.
    tree * tree1;
    *Start thread to visualise tree* (see code below)

    Mutex Lock:
         delete tree1;
         tree1 = newTree();
    Mutex Unlock

可视化器.cpp

visualise(Tree *) {
    Forever:
    Mutex Lock:
         Check for tree change.
         Update tree image.
         Display tree image.
    Mutex Unlock

我想知道这是否可能:

  1. 没有全局范围互斥锁。
  2. 如果可能,不要将互斥锁传递给可视化函数。

我知道这可能不合理,如果不是,我将如何使用 extern 将全局范围变量获取到 visualiser.cpp?

另外,如果我需要将互斥锁传递给函数,我该怎么做呢?

【问题讨论】:

    标签: c++ thread-safety pthreads mutex


    【解决方案1】:

    是的,只要互斥锁在任何线程使用它时保持在范围内,它就不必是全局的。你确实必须告诉第二个线程互斥锁在哪里,不幸的是没有办法解决这个问题。

    传递它与传递任何其他变量没有什么不同。

    因此,只需在第一个线程中定义并初始化它,然后在创建第二个线程时,将其地址作为线程参数传递。

    然后第二个线程可以使用该地址访问互斥体。


    就您希望函数既可用作线程又可用作普通函数的评论而言,由于复杂性,我会避免这样做。

    可以做的就是把大部分工作放到一个普通函数中,然后让线程函数成为一个简单的包装器。你甚至可以传入一个互斥指针,如果有效则可以使用,如果为 NULL 则不使用。

    有关详细信息,请参阅以下完整程序。首先,一些支持的东西,需要的头文件和日志功能:

    #include <pthread.h>
    #include <stdio.h>
    #include <time.h>
    
    static void mylog (int indent, char *s) {
        int i;
        time_t now = time (NULL);
        struct tm *lt = localtime (&now);
        printf ("%02d:%02d:%02d ", lt->tm_hour, lt->tm_min, lt->tm_sec);
        putchar ('|');
        for (i = 0; i < indent; i++) printf ("%-20s|", "");
        printf ("%-20s|", s);
        for (i = indent + 1; i < 3; i++) printf ("%-20s|", "");
        putchar ('\n');
    }
    

    接下来,将完成工作的函数。这是以这样一种方式构建的,它可以从任何线程调用,如果你希望它使用一个互斥指针,可以传递一个互斥指针:

    static void *myfunction (void *ptr) {
        pthread_mutex_t *pMtx = ptr;
    
        mylog (2, "starting");
    
        if (pMtx != NULL) {
            mylog (2, "locking mutex");
            pthread_mutex_lock (pMtx);
            mylog (2, "locked mutex");
        }
    
        mylog (2, "sleeping");
        sleep (5);
        mylog (2, "finished sleeping");
    
        if (pMtx != NULL) {
            mylog (2, "unlocking mutex");
            pthread_mutex_unlock (pMtx);
        }
    
        mylog (2, "stopping");
    }
    

    然后是一个实际的线程函数,它实际上是对上述工作函数的一个薄包装。请注意,它通过特定于线程的参数接收互斥锁并将其传递给工作函数:

    static void *mythread (void *ptr) {
        mylog (1, "starting");
    
        mylog (1, "call fn with mutex");
        myfunction (ptr);
        mylog (1, "and back");
    
        mylog (1, "stopping");
    }
    

    最后,主要功能。这首先调用没有互斥锁的工作函数,然后创建一个互斥锁用于与其他线程共享:

    int main (void) {
        pthread_mutex_t mtx;
        pthread_t tid1;
        char buff[100];
    
        printf ("         |%-20s|%-20s|%-20s|\n", "main", "thread", "workfn");
        printf ("         |%-20s|%-20s|%-20s|\n", "====", "======", "======");
    
        mylog (0, "starting");
    
        mylog (0, "call fn, no mutex");
        myfunction (NULL);
        mylog (0, "and back");
    
        mylog (0, "initing mutex");
        pthread_mutex_init (&mtx, NULL);
    
        mylog (0, "locking mutex");
        pthread_mutex_lock (&mtx);
        mylog (0, "locked mutex");
    
        mylog (0, "starting thead");
        pthread_create (&tid1, NULL, mythread, &mtx);
    
        mylog (0, "sleeping");
        sleep (5);
        mylog (0, "sleep done");
    
        mylog (0, "unlocking mutex");
        pthread_mutex_unlock (&mtx);
    
        mylog (0, "joining thread");
        pthread_join (tid1, NULL);
        mylog (0, "joined thread");
    
        mylog (0, "exiting");
        return 0;
    }
    

    您可以在输出中看到代码本身是如何排序的:

             |main                |thread              |workfn              |
             |====                |======              |======              |
    15:07:10 |starting            |                    |                    |
    15:07:10 |call fn, no mutex   |                    |                    |
    15:07:10 |                    |                    |starting            |
    15:07:10 |                    |                    |sleeping            |
    15:07:15 |                    |                    |finished sleeping   |
    15:07:15 |                    |                    |stopping            |
    15:07:15 |and back            |                    |                    |
    15:07:15 |initing mutex       |                    |                    |
    15:07:15 |locking mutex       |                    |                    |
    15:07:15 |locked mutex        |                    |                    |
    15:07:15 |starting thead      |                    |                    |
    15:07:15 |sleeping            |                    |                    |
    15:07:15 |                    |starting            |                    |
    15:07:15 |                    |call fn with mutex  |                    |
    15:07:15 |                    |                    |starting            |
    15:07:15 |                    |                    |locking mutex       |
    15:07:20 |sleep done          |                    |                    |
    15:07:20 |unlocking mutex     |                    |                    |
    15:07:20 |joining thread      |                    |                    |
    15:07:20 |                    |                    |locked mutex        |
    15:07:20 |                    |                    |sleeping            |
    15:07:25 |                    |                    |finished sleeping   |
    15:07:25 |                    |                    |unlocking mutex     |
    15:07:25 |                    |                    |stopping            |
    15:07:25 |                    |and back            |                    |
    15:07:25 |                    |stopping            |                    |
    15:07:25 |joined thread       |                    |                    |
    15:07:25 |exiting             |                    |                    |
    

    特别注意不带互斥锁的直接调用与使用互斥锁的调用相比的作用。

    【讨论】:

    • 如何还能正常使用可视化功能,即。我只希望它有时被线程化,其他时候我不需要互斥体。
    • @Ben,我不会那样做,这会使你的代码变得非常复杂。我会重构它,以便大部分工作都在一个可以调用的函数中,传递互斥体地址或 NULL。然后,如果线程正在运行,则任何线程都可以使用真正的互斥锁调用该函数,如果不是,则可以调用 NULL,并且该函数将根据传入的地址决定是否锁定。换句话说,将可视化器 thread 与可视化器 函数分开。 前者实际上是与 main 一起运行的线程。后者是一个可以被任何线程调用的函数,但作为一个函数!
    • 听起来不错,我会试一试。我能问一下,为什么没有人支持这个问题。它很详细,有明确的研究,很具体。怎么了?
    • 我在将 pthread_mutex_t 的默认值设置为 NULL 时遇到问题:“pthread_mutex_t treeLock”的默认参数类型为“int”
    • @Ben,您使用的是互斥体的地址,而不是互斥体本身。这是允许您使用 NULL 哨兵值的唯一方法。通常创建互斥锁mtx,但将NULL&amp;mtx传递给函数或其他线程。从这些地方,您检查 NULL,如果没有,则取消引用指针。
    【解决方案2】:

    一旦您将代码从伪代码转换为具体代码,答案就会变得显而易见。

    例如:

    您的可视化线程必须“检查树变化”。

    你是怎么做到的?你必须有一个可以告诉你这些信息的数据结构。这个 ds 将由主线程更新。

    因此,您将互斥锁保留在该数据结构中。它可以是全局的或堆上的

    【讨论】:

    • 我已经实现了所有的代码,非常复杂,所以我只提供了伪代码。感谢您的建议。
    【解决方案3】:

    实际上,在每个线程中传入和使用不同的互斥体实例化不是线程安全的,因为这不会同步多个线程对所有相关线程共有的对象的访问。

    一般来说,每个对象/数据结构实例都应该有一个互斥锁,而不是每个线程一个。话虽如此,需要同步的对象具有互斥体属性更有意义,并在需要同步的方法中对其进行内部管理。

    关于全局互斥锁的提及,并考虑到我之前的段落,互斥锁对于正在同步的对象和所涉及的线程的上下文应该有效地是全局的。也就是说,相同的互斥锁应该是通用的,并且对所有这些实体都可用。这可以在不将其设为全局变量的情况下实现,尽管如前所述。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-06-06
      • 2018-05-23
      • 1970-01-01
      • 2014-09-26
      • 1970-01-01
      • 2015-07-20
      相关资源
      最近更新 更多