【问题标题】:How do I synchronize the threads without using pthread_join()?如何在不使用 pthread_join() 的情况下同步线程?
【发布时间】:2019-06-15 08:36:52
【问题描述】:

我创建了一个用于记录的简单 c++ 程序。我在 for 循环中创建线程,该循环在驱动程序文件 test.cpp 中运行 1000 次。 pthread_create 调用tt.cpp 中的打印函数,它将输入参数写入file.txt。 我希望同步线程。

我尝试使用 pthread_join 同步线程,但我希望在不使用连接的情况下同步线程。

我知道如果一个线程被锁定,很多线程会一直等待直到它被解锁,在那个线程被解锁之后,任何一个等待的线程都会锁定这个函数。因此,我尝试在tt.cpp 中使用静态整数变量,并尝试将其与输入参数进行比较,以便可以使用pthread_cond_waitpthread_wait_signal,但比较时出现分段错误。

 /* if(j == *((int *)input)))
    This comparison gave me a segmentation fault */


-------------------------test.cpp---------------------------------
#include "thr.h"


pthread_mutex_t loc;
FILE *thePrintFile = NULL;

int main()
{
    pthread_mutex_init(&loc,NULL);

    pthread_t p;
    thePrintFile = fopen("file.txt","r+");
    for(int i =0; i<1000;i++)
    {
        pthread_create(&p,NULL,print,(void *)i);
    }
    for(int k = 0; k<100000;k++);
    /* i have used it to ensure that the main thread 
       doesn't exit before the pthreads finish writing */
    return 0;

}

    ------------------------tt.cpp------------------------------------
    #include "thr.h"

    extern pthread_mutex_t loc;
    extern FILE *thePrintFile;

    void* print(void *input)
    {
        if(pthread_mutex_trylock(&loc) == 0)
        {   

            fprintf(thePrintFile,"%s%d\n",(int *)input);
            fflush(thePrintFile);
            pthread_mutex_unlock(&loc);
        }   
    }

    -----------------------------thr.h--------------------------------
    #ifndef THR_H_INCLUDED
    #define THR_H_INCLUDED

    void* print(void *input);

    #endif

下面给出的是 file.txt 的一部分。

1
5
9
10
11
12
13
14
15
16
18
19
20
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
21
201
202

【问题讨论】:

  • 问题是什么?
  • 对不起,如果我的问题不清楚。我希望线程按顺序工作,比如首先它应该写 0 然后 1,2,3 .....等等。
  • 您表明您在某条线路上遇到了分段错误。该行不在您发布的实际代码中。然后你的打印部分有一个 void * 返回类型,但你什么都不返回。
  • 是 C 还是 C++ ?您将其标记为 C++,但代码更像 C
  • 创建 1000 个线程可能不是一个好主意。

标签: c++ synchronization pthreads mutex


【解决方案1】:

要正确同步线程,您需要使用同步设备,例如condition variable。由于您使用的是 C++,因此最好使用它的 built-in threads 而不是原始 pthread API。

例如,此代码同步 1000 个线程,以便每个线程使用条件变量和互斥锁对打印自己的 ID,以确保按顺序打印 ID,由所有人共享的单独变量跟踪。它不是很有效,因为所有线程都在争夺同一个互斥锁,但它可以正常工作。通过为每个线程创建一个条件变量可以提高效率,但正确地执行此操作需要更好地了解您的实际用例。

#include <thread>
#include <mutex>
#include <condition_variable>
#include <iostream>
#include <vector>

static std::mutex mutex;
static std::condition_variable condvar;
static int whos_next;

static void work(int id) {
  std::unique_lock<std::mutex> lock{mutex};
  // wait for our turn
  condvar.wait(lock, [=]() { return whos_next == id; });
  // it's our turn now - print our thread ID
  std::cout << id << '\n';
  ++whos_next;
  condvar.notify_all();    // notify the next thread to run
}

int main() {
  std::vector<std::thread> threads;
  for (int i = 0; i < 1000; i++)
    threads.push_back(std::thread([=]() { work(i); }));

  for (auto &t: threads)
    t.join();
}

请注意,上面的代码使用join并不是为了让线程彼此同步(它们之间使用互斥锁/条件进行同步),而是为了它的预期目的:等待线程在退出之前完成@ 987654326@。 C++ 甚至要求您在破坏 std::thread 对象之前执行此操作,因此删除连接会导致程序终止。您可以轻松证明线程不依赖join 进行同步,例如通过在加入之前插入睡眠,或者以相反的顺序加入它们。

【讨论】:

  • 感谢您的帮助!有什么方法可以使用 pthreads 来完成,或者可以在不使用 t.join() 的情况下同步此代码吗?
  • @AkshitAchara 在问题的 cmets 中,您明确表示您的代码是 C++,所以我给了您一个 C++ 答案。然而,将它移植到 pthreads 应该很简单,这些 pthreads 也支持使用 condition variables and mutexes 进行同步。
  • 您的回答达到了目的,但您正在使用 join。如果我删除了 join 语句。输出:0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 在没有活动异常的情况下终止调用 Aborted (core dumped) 。这是因为 main 在线程停止执行之前就终止了吗?
  • @AkshitAchara 是的,这就是你被丢弃核心的原因。您可以通过等待另一个锁来替换此答案中的t.join(),当work() 完成时,通知该锁结束程序。
  • @AkshitAchara 这里join 用于其预期目的:等待线程完成。 (C++ 甚至要求您在破坏 std::thread 对象之前执行此操作,这就是为什么您在删除连接时会崩溃的原因。)您可以在连接之前插入睡眠以说服自己我没有“作弊”,即线程正确同步,即使它们仅在它们已经退出后才“加入”。
【解决方案2】:

我在您的代码中发现了多个问题。

  • 您表明您在某条线路上遇到了分段错误。该行不在您发布的实际代码中。
  • 您的打印部分有一个 void * 返回类型,但您没有返回任何内容。
  • 您将 int 转换为 void *。指针的大小可能与 int 的大小不同。将您的打印函数参数更改为 int 。那么演员阵容就没有必要了。
  • 您不能同时将 1000 个线程写入同一个文件。所以在这种情况下,没有理由创建线程,因为无论如何您都必须按顺序执行它们。但如果你这样做了,那么你必须加入他们。

那么让我们看看你尝试做的比较:

if(j == *((int *)input)))

j 显然是 int。输入无效*。在基础输入是 int。因为不能保证将 int 转换为 void * 是安全的,所以将其转换回也不安全。想象一下 int 是 64 位,而指针是 32 位。如果您将 int 转换为 void * 您将丢失 32 位。然后你从 32 位转换到 64 位,所以你添加了 32 位的 0。显然这会带来问题。

考虑使用 C++11,这将大大改进您的代码。 https://solarianprogrammer.com/2011/12/16/cpp-11-thread-tutorial/

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-08
    • 2014-11-22
    • 1970-01-01
    • 2022-07-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多