如何在不使用 pthread_join() 的情况下同步线程?
How do I synchronize the threads without using pthread_join()?
我创建了一个用于日志记录的简单 C++ 程序。我在 for 循环中创建线程,该循环在驱动程序文件 test.cpp
中运行 1000 次。 pthread_create
调用 tt.cpp
中的打印函数,该函数将输入参数即 i,写入 file.txt
中。
我希望同步线程。
我已经尝试使用 pthread_join
来同步线程,但我希望不使用 join 来同步线程。
我知道如果一个线程被锁定,很多线程将等待直到它被解锁,并且在该线程被解锁之后,任何一个等待的线程都会锁定该函数。因此,我尝试在 tt.cpp
中使用静态整数变量并尝试将其与输入参数进行比较,以便我可以使用 pthread_cond_wait
和 pthread_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
我发现您的代码中存在多个问题。
- 你表明你在某行上遇到了段错误。该行不在您发布的实际代码中。
- 您的打印部分有一个 void * return 类型,但您没有 return 任何内容。
- 您将 int 转换为 void *。指针的大小可能与 int 的大小不同。将您的打印函数参数更改为 int 。那么演员就没必要了。
- 您不能同时使用 1000 个线程写入同一个文件。所以在这种情况下没有理由创建线程,因为无论如何你都必须按顺序执行它们。但如果你这样做,那你就必须加入他们。
然后让我们看看您尝试进行的比较:
if(j == *((int *)input)))
j 显然是整数。输入为空*。基础输入是 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/
要正确同步线程,您需要使用诸如 condition variable. Since you are using C++, you are much better off using its 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
并不是为了使线程彼此同步(它们使用 mutex/condition 在它们之间进行同步),而是出于其预期目的:等待退出前要完成的线程 main()
。 C++ 甚至要求您在销毁 std::thread
对象之前执行此操作,因此删除连接会导致程序终止。您可以轻松证明线程不依赖 join
进行同步,例如通过在加入之前插入一个睡眠,或者以相反的顺序加入它们。
我创建了一个用于日志记录的简单 C++ 程序。我在 for 循环中创建线程,该循环在驱动程序文件 test.cpp
中运行 1000 次。 pthread_create
调用 tt.cpp
中的打印函数,该函数将输入参数即 i,写入 file.txt
中。
我希望同步线程。
我已经尝试使用 pthread_join
来同步线程,但我希望不使用 join 来同步线程。
我知道如果一个线程被锁定,很多线程将等待直到它被解锁,并且在该线程被解锁之后,任何一个等待的线程都会锁定该函数。因此,我尝试在 tt.cpp
中使用静态整数变量并尝试将其与输入参数进行比较,以便我可以使用 pthread_cond_wait
和 pthread_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
我发现您的代码中存在多个问题。
- 你表明你在某行上遇到了段错误。该行不在您发布的实际代码中。
- 您的打印部分有一个 void * return 类型,但您没有 return 任何内容。
- 您将 int 转换为 void *。指针的大小可能与 int 的大小不同。将您的打印函数参数更改为 int 。那么演员就没必要了。
- 您不能同时使用 1000 个线程写入同一个文件。所以在这种情况下没有理由创建线程,因为无论如何你都必须按顺序执行它们。但如果你这样做,那你就必须加入他们。
然后让我们看看您尝试进行的比较:
if(j == *((int *)input)))
j 显然是整数。输入为空*。基础输入是 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/
要正确同步线程,您需要使用诸如 condition variable. Since you are using C++, you are much better off using its 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
并不是为了使线程彼此同步(它们使用 mutex/condition 在它们之间进行同步),而是出于其预期目的:等待退出前要完成的线程 main()
。 C++ 甚至要求您在销毁 std::thread
对象之前执行此操作,因此删除连接会导致程序终止。您可以轻松证明线程不依赖 join
进行同步,例如通过在加入之前插入一个睡眠,或者以相反的顺序加入它们。