文章目录
- 线程创建
- 线程等待
- 终止线程
- 利用多线程求和(单进程多线程)
- 获取线程ID
- 取消线程
- 线程分离
- 共享?
线程创建
创建线程需要用的函数是pthread_create。函数原型如下:
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
参数说明:
- thread:获取创建成功的线程ID,该参数是一个输出型参数。
- attr:用于创建线程的属性,传入NULL表示使用默认属性。
- start_routine:该参数是一个函数地址,表示线程例程,即线程启动后要执行的函数。
- arg:传给线程例程的参数。
创建线程代码示例:以下有一个主线程,并创建了一个新线程,主线程打印了pthread_create函数的第一个输出型参数,即新线程的id。
运行结果:
演示:以下代码有一个主线程,利用snprintf对每个新线程进行了命名,并通过pthread_create函数创建了10个新线程,同时给thread_run函数传了参数tname。
运行结果:
线程等待
线程和进程一样,也是需要等待的,否则就会类似产生“僵尸进程”,产生内存泄漏的问题。等待线程用的是pthread_join函数,函数原型如下:
int pthread_join(pthread_t thread, void **retval);
参数说明:
- thread:被等待线程的ID。
- retval:线程退出时的退出码信息。
- 线程等待成功返回0,失败返回错误码。
注意:pthread_join属于阻塞等待。
代码示例:
终止线程
如果需要只终止某个线程而不是终止整个进程,有三种方法:
- 从线程函数return。
- 线程可以调用pthread_exit函数终止自己。
- 一个线程可以调用pthread_cancel函数终止同一进程中的另一个线程。
利用多线程求和(单进程多线程)
1 #include<iostream>
2 #include<string>
3 #include<unistd.h>
4 #include<pthread.h>
5 #include<ctime>
6
7 #define NUM 10
8
9 using namespace std;
10
11 enum
12 {
13 OK=0,
14 ERROR
15 };
16
17 class ThreadData
18 {
19 public:
20 ThreadData(const string& name,int id,time_t createTime,int top)
21 :_name(name)
22 ,_id(id)
23 ,_createTime((uint64_t)createTime)
24 ,_status(OK)
25 ,_top(top)
26 ,_result(0)
27 {}
28 ~ThreadData()
29 {}
30 public:
31 //输入的
32 string _name;//线程名
33 int _id;//线程id
34 uint64_t _createTime;//线程创建时间
35
36 //返回的
37 int _status;//线程状态
38 int _top;//求和至哪个整数
39 int _result;//返回求和结果
40 };
41 void *thread_run(void *args)
42 {
43 ThreadData *td = static_cast<ThreadData *>(args);
44
45 for(int i = 1; i <= td->_top; i++)
46 {
47 td->_result += i;
48 }
49 cout << td->_name << " cal done!" << endl;
50 return td;
51
52 // while (true)
53 // {
54 // cout << "thread is running, name " << td->_name << " create time: " << td->_createTime << " index: " << td->_id << endl;
55 // // // exit(10); // 进程退出,不是线程退出,只要有任何一个线程调用exit,整个进程(所有线程)全部退出!
56 // // sleep(4);
57
58 // // break;
59 // }
60 // delete td;
61
62 // pthread_exit((void*)2); // void *ret = (void*)1;
63 // return nullptr;
64 }
65 int main()
66 {
67
68 //1.创建线程
69 pthread_t tids[NUM];
70
71 for(int i=0;i<NUM;i++)
72 {
73 char tname[64];
74 //1.1将字符串格式化进tname
75 snprintf(tname,64,"thread-%d",i+1);
76 //1.2创建类对象:线程名,线程id,线程创建的时间戳,top表示求和到哪个整数
77 ThreadData* td = new ThreadData(tname,i+1,time(nullptr),100+5*i);
78
79 //1.3
80 //thread:获取创建成功的线程ID,该参数是一个输出型参数。
81 //创建线程的属性,传入NULL表示使用默认属性
82 //线程启动后要执行的函数,这里用的是thread_run函数
83 //传给线程例程的参数,我们自定义了一个求和整数的类对象
84 pthread_create(tids+i,nullptr,thread_run,td);//创建线程,td是thread_run函数的参数,
85 //同时td也是我们自己创建的类对象
86 sleep(1);
87 }
88
89 //2.等待线程
90 void *ret = nullptr;
91 for(int i=0;i<NUM;i++)
92 {
93 //int pthread_join(pthread_t thread, void **retval);,注意是void**类型的
94 //被等待线程的id,获取线程退出码
95 int n = pthread_join(tids[i],&ret);
96
97 if(n!=0)
98 {
99 cerr << "pthread_join error" << endl;
100 }
101 //获取线程退出码,将ret强制转换成ThreadData*
102 ThreadData* td = static_cast<ThreadData*>(ret);
103
104 if(td->_status == OK)
105 {
106 cout << td->_name << " 计算的结果是: " << td->_result << " (它要计算的是[1, " << td->_top << "])" <<endl;
107 }
108 delete td;
109 }
110 cout << "all thread quit..." << endl;
111 return 0;
112 }
运行结果:
获取线程ID
常见获取线程ID的方式有两种:
- 创建线程时通过输出型参数获得。
- 通过调用pthread_self函数获得。调用pthread_self函数即可获得当前线程的ID,类似于调用getpid函数获取当前进程的ID。
pthread_self函数原型如下:
pthread_t pthread_self(void);
取消线程
pthread_cancel函数可以取消线程,pthread_cancel函数的函数原型如下:
int pthread_cancel(pthread_t thread);//thread是被取消线程的ID。
代码实例:
#include<iostream>
#include<unistd.h>
#include<pthread.h>
#include<string>
#include<ctime>
using namespace std;
void* thread_run(void* args)
{
const char* name = static_cast<const char*>(args);
int cnt=5;
while(cnt)
{
cout<<name<<"is running"<<cnt--<<"获取该线程id"<<pthread_self()<<endl;
sleep(1);
}
//5s后就终止线程
pthread_exit((void*)11);
}
int main()
{
pthread_t tid;
//创建单进程单线程
pthread_create(&tid,nullptr,thread_run,(void*)"thread1");
sleep(3);//睡眠3s就取消线程
pthread_cancel(tid);
void* ret=nullptr;
//等待线程
pthread_join(tid,&ret);
cout<<"new thread exit:"<<(int64_t)ret<<"退出线程"<<tid<<endl;
return 0;
}
线程是可以取消自己的,取消成功的线程的退出码一般是-1。
线程分离
线程分离的函数:
int pthread_detach(pthread_t thread);//thread是所要分离线程的ID。
- 默认情况下,新创建的线程是joinable(需要主线程等待回收资源的)的,线程退出后,需要对其进行pthread_join操作,否则无法释放资源,从而造成内存泄漏。
- 但如果我们不关心线程的返回值,join也是一种负担,此时我们可以将该线程进行分离,后续当线程退出时就会自动释放线程资源。这里的意思是不再需要主线程去join了,当这个线程退出时系统会自动回收该线程所对应的资源。
- 可以是线程组内其他线程对目标线程进行分离,也可以是线程自己分离。
- 线程分离之后,就不能join了,不然会报错。
- joinable和分离是冲突的,一个线程不能既是joinable又是分离的。
代码示例:
创建五个新线程后让这五个新线程将自己进行分离,主线程就不需要在对这五个新线程进行join了,当这5个线程退出时系统会自动回收该线程所对应的资源。
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/types.h>
void* Routine(void* arg)
{
pthread_detach(pthread_self());
char* msg = (char*)arg;
int count = 0;
while (count < 5){
printf("I am %s...pid: %d, ppid: %d, tid: %lu\n", msg, getpid(), getppid(), pthread_self());
sleep(1);
count++;
}
pthread_exit((void*)6666);
}
int main()
{
pthread_t tid[5];
for (int i = 0; i < 5; i++){
char* buffer = (char*)malloc(64);
sprintf(buffer, "thread %d", i);
pthread_create(&tid[i], NULL, Routine, buffer);
printf("%s tid is %lu\n", buffer, tid[i]);
}
while (1){
printf("I am main thread...pid: %d, ppid: %d, tid: %lu\n", getpid(), getppid(), pthread_self());
sleep(1);
}
return 0;
}
共享?
- 全局变量是所有线程共享的,如果不想它被所以线程共享,需要加上
__thread
修饰变量。 - 线程中的局部变量不是所有线程共享的。
同一线程内的局部变量地址是一致的,不同线程的局部变量是不共享的。
代码示例:
#include<iostream>
#include<string>
#include<pthread.h>
#include<unistd.h>
using namespace std;
void *threadRoutine(void* args)
{
string name = static_cast<const char*>(args);
int cnt = 5;
while(cnt)
{
cout << name << " : " << cnt-- << " : " << pthread_self() << " &cnt: " << &cnt << endl;
//cout << name << " g_val: " << g_val++ << ", &g_val: " << &g_val << endl;
sleep(1);
}
return nullptr;
}
int main()
{
//线程创建
pthread_t t1,t2,t3;
pthread_create(&t1,nullptr,threadRoutine,(void*)"thread 1");
pthread_create(&t2,nullptr,threadRoutine,(void*)"thread 2");
pthread_create(&t3,nullptr,threadRoutine,(void*)"thread 3");
//线程等待
pthread_join(t1,nullptr);
pthread_join(t2,nullptr);
pthread_join(t3,nullptr);
return 0;
}
一个进程内的全局变量是所有线程共享的。
#include<iostream>
#include<string>
#include<pthread.h>
#include<unistd.h>
using namespace std;
int g_val = 100;
// std::string hexAddr(pthread_t tid)
// {
// g_val++;
// char buffer[64];
// snprintf(buffer, sizeof(buffer), "0x%x", tid);
// return buffer;
// }
void *threadRoutine(void* args)
{
string name = static_cast<const char*>(args);
int cnt = 5;
while(cnt)
{
//cout << name << " : " << cnt-- << " : " << pthread_self() << " &cnt: " << &cnt << endl;
cout << name << " g_val: " << g_val++ << ", &g_val: " << &g_val << endl;
sleep(1);
}
return nullptr;
}
int main()
{
pthread_t t1, t2, t3;
pthread_create(&t1, nullptr, threadRoutine, (void*)"thread 1"); // 线程被创建的时候,谁先执行不确定!
pthread_create(&t2, nullptr, threadRoutine, (void*)"thread 2"); // 线程被创建的时候,谁先执行不确定!
pthread_create(&t3, nullptr, threadRoutine, (void*)"thread 3"); // 线程被创建的时候,谁先执行不确定!
pthread_join(t1, nullptr);
pthread_join(t2, nullptr);
pthread_join(t3, nullptr);
return 0;
}
用__thread修饰全局变量,这个全局变量就不会被所以线程共享了。