实习项目经验总结4:LINUX系统下的C++线程使用

  记录LINUX系统下的C++线程使用,不涉及保密项目。

  c++ 11 之后有了标准的线程库:std::thread。但在LINUX下pthread使用的多一点,因此本文主要学习pthread的一些用法。

基本概念

线程基本概念待续。。。。

线程的基本使用

线程的创建与退出

函数原型:int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine)(void*), void *arg);
--- thread: 线程标识符,即线程ID,标识唯一线程;
--- attr    : 一个不透明的属性对象,可以被用来设置线程属性。您可以指定线程属性对象,也可以使用默认值 NULL;
--- start_routine: 线程运行函数起始地址,一旦线程被创建就会执行;
--- arg: 运行函数的参数。它必须通过把引用作为指针强制转换为 void 类型进行传递。如果没有传递参数,则使用 NULL;
--- 创建成功,返回0;失败,则返回错误号。
函数原型:void pthread_exit(void *value_ptr);
--- 没有返回值。最后一个线程终止后,该进程以状态0退出;
pthread_study1.cpp
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
30
31
32
33
34
35
36
37
38
39
40
41
42
/*
* Author: xiaoxiaotaoya
* Data: 2020-05-23
* 内容:学习线程的创建和退出,以及数组的参数传递。
* tao@ubuntu:~/Desktop/study/thread c++$ g++ pthread_study1.cpp -lpthread -o pthread_study1.o
* tao@ubuntu:~/Desktop/study/thread c++$ ./pthread_study1.o
*/

#include <iostream>
#include <cstdlib>
#include <pthread.h>

using namespace std;
#define NUM_THREADS 5 //定义线程数量

//线程函数
void *PrintHello(void *threadid)
{
// 对传入的参数进行强制类型转换,由无类型指针变为整形数指针,然后再读取
int tid = *((int*)threadid);
cout << "Hello Pthread!" << endl;
cout << "当前线程 ID: " << tid << endl;
}

//主函数
int main ()
{
pthread_t threads[NUM_THREADS];
int indexes[NUM_THREADS];// 用数组来保存i的值
int i, rec;
for( i=0; i < NUM_THREADS; i++ ){
cout << "创建线程:" << i << endl;
indexes[i] = i; //先保存i的值
// 传入的时候必须强制转换为void* 类型,即无类型指针
rec = pthread_create(&threads[i], NULL, PrintHello, (void *)&(indexes[i]));
if (rec){
cout << "Error:无法创建线程," << rec << endl;
exit(-1);
}
}
pthread_exit(NULL);
}
pthread_study1 运行结果
pthread_study2.cpp
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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
/*
* Author: xiaoxiaotaoya
* Data: 2020-05-23
* 内容:学习结构的参数传递。
* tao@ubuntu:~/Desktop/study/thread c++$ g++ pthread_study2.cpp -lpthread -o pthread_study2.o
* tao@ubuntu:~/Desktop/study/thread c++$ ./pthread_study2.o
*/

#include <iostream>
#include <cstdlib>
#include <pthread.h>

using namespace std;
#define NUM_THREADS 5 //定义线程数量

//创建结构体 thread_data
struct thread_data{
int thread_id;
char *message;
};

//线程函数
void *PrintHello(void *threadarg)
{
struct thread_data *my_data;
my_data = (struct thread_data *) threadarg;
cout << "Thread ID : " << my_data->thread_id << endl;
cout << " Message : " << my_data->message << endl;
pthread_exit(NULL);
}

//主函数
int main ()
{
pthread_t threads[NUM_THREADS];
struct thread_data td[NUM_THREADS];
int i,rec;

for( i=0; i < NUM_THREADS; i++ ){
cout <<"main() : creating thread, " << i << endl;
td[i].thread_id = i;
td[i].message = (char*)"This is message";
rec = pthread_create(&threads[i], NULL, PrintHello, (void *)&td[i]);
if (rec){
cout << "Error:unable to create thread," << rec << endl;
exit(-1);
}
}
pthread_exit(NULL);
}
pthread_study2 运行结果

线程的阻塞与分离

  • 线程的阻塞 pthread_join
    功能:阻塞线程,当子线程任务执行完后,才能继续执行主线程任务;
函数原型:int pthread_join(pthread_t thread, void **value_ptr);
--- thread: 线程标识符,即线程ID,标识唯一线程;
--- value_ptr: 用户定义的指针,用来存储被等待线程的返回值;
--- 创建成功,返回0;失败,则返回错误号。
  • 线程的分离 pthread_detach
    功能:主线程与子线程分离,子线程结束后,资源自动回收。
函数原型:int pthread_detach(pthread_t thread);
--- thread: 线程标识符,即线程ID,标识唯一线程;
--- 创建成功,返回0;失败,则返回错误号。
pthread_study3.cpp
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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
/*
* Author: xiaoxiaotaoya
* Data: 2020-05-23
* 内容:学习线程的阻塞与分离,以及整数的参数传递。
* tao@ubuntu:~/Desktop/study/thread c++$ g++ pthread_study3.cpp -lpthread -o pthread_study3.o
* tao@ubuntu:~/Desktop/study/thread c++$ ./pthread_study3.o
*/

#include <iostream>
#include <stdio.h> //使用printf
#include <cstdlib>
#include <pthread.h>

using namespace std;
#define INDEX 3 //定义线程数量

static int count = 0;

//线程函数
void *Thread_Run(void *threadid){
pthread_detach(pthread_self()); //分离创建的子线程自己,该子线程终止时底层资源立即被回收
int tid = *((int*)threadid);
cout << "当前线程 ID: " << tid << endl;
for(int i =0; i<5; i++){
count++;
printf("Current count is %d\n",count);
}
}

//主函数
int main(void)
{
pthread_t ThreadID[INDEX];
for(int i=0; i < INDEX; i++ ){
//按顺序创建线程
pthread_create(&ThreadID[i],NULL,Thread_Run,(void *)&(i));
printf("创建线程INDEX:%d\n",i);
//阻塞线程
int rec = pthread_join(ThreadID[i],NULL);
if(rec){
printf("线程阻塞失败!\n");
abort();
}
}
//查看阻塞效果
printf("The count is: %d\n",count);
pthread_exit(NULL); //退出线程
return 0;
}
pthread_study3 运行结果

  可以看到线程阻塞后,按照子线程ID的顺序执行任务。当子线程任务执行完后,才开始执行主线程任务。

  在默认情况下通过pthread_create函数创建的线程是非分离属性的,由pthread_create函数的第二个参数决定,在非分离的情况下,当一个线程结束的时候,它所占用的系统资源并没有完全真正的释放,也没有真正终止。只有在pthread_join函数返回时,该线程才会释放自己的资源。或者是设置在分离属性的情况下,一个线程结束会立即释放它所占用的资源。
  通常是主线程使用pthread_create()创建子线程以后,一般可以调用pthread_detach(threadid)分离刚刚创建的子线程,这里的threadid是指子线程的threadid;如此以来,该子线程止时底层资源立即被回收;被创建的子线程也可以自己分离自己,子线程调用pthread_detach(pthread_self())就是分离自己,因为pthread_self()这个函数返回的就是自己本身的线程ID。

线程执行的互斥和同步

未完待续

信号量控制线程的执行顺序

未完待续

参考

+