同⼀地址空间,因此TextSegment
、DataSegment
都是共享的,如果定义⼀个函数,在各线程中都可以调⽤,如果定义⼀个全局变量,在各线程中都可以访问到,除此之外,各线程还共享以下进程资源和环境:
进程和线程的关系如下图:
linux如何看待之前学习的单进程?具有⼀个线程执⾏流的进程
在Linux中,单进程是资源分配基本单位,有独立内存与CPU时间片,由PCB管理。其指令顺序执行,阻塞操作会致进程暂停。单进程难以利用多核并行,实现并发受限。它独立性高,崩溃不影响其他进程,但容错性差,逻辑简单利于调试维护。
功能:创建一个新的线程原型:
int pthr/ead_create(pthread_t *thread,const pthread_attr_t *attr,void *(*start_routine) (void*), void *arg);
参数:
thread:返回线程ID
attr:设置线程的属性,attr为NULL表示使用默认属性start_routine:是个函数地址,线程启动后要执行的函数arg:传给线程启动函数的参数
返回值:成功返回0;失败返回错误码
错误检查:
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <pthread.h>
void *rout(void *arg)
{
int i;
for (;;)
{
printf("I'am thread 1\n");
sleep(1);
}
}
int main(void)
{
pthread_t tid;
int ret;
if ((ret = pthread_create(&tid, NULL, rout, NULL)) != 0)
{
fprintf(stderr, "pthread_create : %s\n", strerror(ret));
exit(EXIT_FAILURE);
}
int i;
for (;;)
{
printf("I'am main thread\n");
sleep(1);
}
}
#include <pthread .h>
//获取线程ID
pthread_t pthread_self(void);
打印出来的tid是通过pthread
库中有函数pthread_self
得到的,它返回一个pthread_t类型的变量,指代的是调用pthread_self
函数的线程的“ID
”。
怎么理解这个“ID”呢?这个“ID”是pthread库给每个线程定义的进程内唯一标识,是pthread库维持的。
由于每个进程有自己独立的内存空间,故此“ID”的作用域是进程级而非系统级(内核不认识)。
其实pthread库也是通过内核提供的系统调用(例如clone)来创建线程的,而内核会为每个线程创建系统全局唯一的“ID”来唯一标识这个线程。
使⽤PS命令查看线程信息 运⾏代码后执⾏:
$ ps -aL | head -1 && ps -aL \ grep mythreadPID LWP TTY
T工ME CMD
2711838 2711838 pts/ 23500:00:00 mythread
2711838 2711839 pts/23500:00:00 mythread
-L选项:打印线程信息
LWP是什么呢?LWP得到的是真正的线程ID。之前使用pthread_self得到的这个数实际上是一个地址,在虚拟地址空间上的一个地址,通过这个地址,可以找到关于这个线程的基本信息,包括线程ID,线程栈,寄存器等属性。
在ps -aL 得到的线程ID,有一个线程ID和进程ID相同,这个线程就是主线程,主线程的栈在虚拟地址空间的栈上,而其他线程的栈在是在共享区(堆栈之间),因为pthread系列函数都是pthread库提供给我们的。而pthread库是在共享区的。所以除了主线程之外的其他线程的栈都在共享区。
如果需要只终⽌某个线程⽽不终⽌整个进程,可以有三种⽅法:
pthread_exit函数
功能:线程终⽌
原型:
void pthread_exit(void *value_ptr);
参数:
value_ptr:value_ptr
不要指向⼀个局部变量。
返回值:
⽆返回值,跟进程⼀样,线程结束的时候⽆法返回到它的调⽤者(⾃⾝)
需要注意,pthread_exit
或者return
返回的指针所指向的内存单元必须是全局的或者是⽤malloc
分配的,不能在线程函数的栈上分配,因为当其它线程得到这个返回指针时线程函数已经退出了。
pthread_cancel函数
功能:取消⼀个执⾏中的线程
原型:
int pthread_cancel(pthread_t thread);
参数:
thread:线程ID
返回值:成功返回 0 ;失败返回错误码
为什么需要线程等待?
功能:等待线程结束
原型
lint pthread_join(pthread_t thread, void **value_ptr);
参数:
thread :线程ID
value_ptr:它指向一个指针,后者指向线程的返回值
返回值:成功返回0;失败返回错误码
调⽤该函数的线程将挂起等待,直到id为thread的线程终⽌。thread线程以不同的⽅法终⽌,通过pthread_join
得到的终⽌状态是不同的,总结如下:
PTHREAD_CANCELED
。#include <stdio.h>
#include <stdlib.h>
void *thread1(void* args)
{
printf("thread 1 returning ... \n");
int *p = (int*)malloc(sizeof(int));
*p = 1;
return (void*)p;
}
void* thread2(void *args)
{
printf("thread 2 exiting .. \n");
int *p = (int*)malloc(sizeof(int));
*p = 2;
pthread_exit((void*)p);
}
void* thread3(void* args)
{
while(1)
{
printf("thread 3 is running ... \n");
sleep(1);
}
return nullptr;
}
int main(void)
{
pthread_t tid;
void *ret;
//thread 1 return
pthread_create(&tid, nullptr, thread1, nullptr);
pthread_join(tid, &ret);
printf("thread return, thread id 0x%lX, return code:%d\n", tid, *(int*)ret);
free(ret);
// thread 2 exit
pthread_create(&tid, nullptr, thread2, nullptr);
pthread_join(tid, &ret);
printf("thread return , thread id 0x%lX, return code:%d\n", tid, *(int*)ret);
free(ret);
// thread 3 cancel by other
pthread_create(&tid, nullptr, thread3, nullptr);
sleep(3);
pthread_cancel(tid);
pthread_join(tid, &ret);
if( ret == PTHREAD_CANCELED)
printf("thread return ,thread id: 0x%lX, return code:PTHREAD_CANCELD\n", tid);
else
printf("thread return , thread id :0x%lX, return code: nullptr", tid);
}
默认情况下,新创建的线程是joinable的,线程退出后,需要对其进⾏pthread_join操作,否则⽆法释放资源,从⽽造成系统泄漏。 如果不关⼼线程的返回值,join是⼀种负担,这个时候,我们可以告诉系统,当线程退出时,⾃动释放线程资源。
int pthread_detach(pthread_t thread);
可以是线程组内其他线程对⽬标线程进⾏分离,也可以是线程⾃⼰分离:
pthread_detach(pthread_self());
joinable和分离是冲突的,⼀个线程不能既是joinable⼜是分离的。
void* thread_run(void* args)
{
pthread_detach(pthread_self());
printf("%s\n", (char*)args);
return nullptr;
}
int main()
{
pthread_t tid;
if(pthread_create(&tid, nullptr, thread_run, (void*)"thread1 run ...") != 0)
{
printf("create thread error\n");
return 1;
}
int ret = 0;
sleep(1); //很重要, 要让线程先分离, 再等待
if(pthread_join(tid, nullptr) == 0)
{
printf("pthread wait success\n");
ret = 0;
}
else
{
printf("pthread wait failed\n");
ret =1;
}
return ret;
}
#include <iostream>
#include <string>
#include <cstdio>
#include <cstring>
#include <unistd.h>
#include <thread>
//线程的局部存储
int shared_value = 100;
std::string toHex(pthread_t tid)
{
//4,进程内的函数,线程共享
char buffer[64];
snprintf(buffer, sizeof(buffer), "0x%lx", tid);
return buffer;
}
void* start(void* args)
{
std::string name = static_cast<const char*>(args);
sleep(1);
while(true)
{
printf("I am a new thread , name: %s, shared_value: %d , &share_value: %p\n", name.c_str(), shared_value, &shared_value );
sleep(1);
}
return nullptr;
}
int main()
{
pthread_attr_t attr;
pthread_t tid;
pthread_create(&tid, nullptr, start, (void*)"thread-1");
std::cout<<"I am a new thread, name: main, "<< toHex(pthread_self())
<<", NEW thread id: " << toHex(tid) << std::endl;
while(true)
{
printf("main thread ,shared_value: %d, &shared_value: %p\n", shared_value, &shared_value);
shared_value += 10;
sleep(1);
}
pthread_join(tid, nullptr);
return 0;
}
while true; do ps -aL | head -1; ps -aL | grep mythread; sleep 1; done
//线程的局部存储
__thread int shared_value = 100;
oid *start1(void *args)
{
std::string name = static_cast<const char *>(args);
int a = 100;
while (true)
{
std::cout << name << " local val a: " << a << std::endl;
a += 100;
if(1000 == a)
{
break;
}
sleep(1);
}
return nullptr;
}
int main()
{
// pthread_t tid1, tid2;
pthread_t tid;
pthread_create(&tid, nullptr, start1, (void *)"thread-1");
//pthread_detach(tid);
sleep(5);
void *ret = nullptr;
int n = pthread_join(tid, &ret); //
std::cout << " new thread exit code: " << (long long int)ret << " , n = " << n << std::endl;
return 0;
}
#include <queue>
class ThreadData
{
public:
ThreadData()
{
}
void Init(const std::string &name, int a, int b)
{
_name = name;
_a = a;
_b = b;
}
void Excute()
{
_result = _a + _b;
}
int Result() { return _result; }
std::string Name() { return _name; }
void SetId(pthread_t tid) { _tid = tid; }
pthread_t Id() { return _tid; }
int A() { return _a; }
int B() { return _b; }
~ThreadData()
{
}
private:
std::string _name;
int _a;
int _b;
int _result;
pthread_t _tid;
};
class outData
{
};
// 5全局变量在线程内部是共享的
int gval = 100;
std::queue<int> q;
char buffer[4096];
#define NUM 10
std::string toHex(pthread_t tid)
{
//进程内的函数, 线程共享
char tranbuffer[64];
snprintf(tranbuffer, sizeof(buffer), "0x%lx", tid);
return tranbuffer;
}
void *routine(void *args)
{
// std::string name = static_cast<const char *>(args);
ThreadData *td = static_cast<ThreadData *>(args);
while (true)
{
// 3,不加保护的情况下, 显示器文件就是共享资源!
std::cout << "我是新线程, 我的名字是: " << td->Name()
<< ", my tid is : " << toHex(pthread_self())
<< ", 全局变量(会修改) : " << gval << std::endl;
gval++;
td->Excute();
sleep(1);
break;
}
//8,返回值问题:返回参数, 可以是变量, 数字, 对象!
// 8.1 理论上, 堆空间也是共享的! 谁拿着堆空间的入口地址,谁就能访问该堆区
// int* p = new int(10);
// return (void*)p;
return td;
}
int main()
{
ThreadData td[NUM];
// 准备我们要加工处理的数据
for (int i = 0; i < NUM; i++)
{
char id[64];
snprintf(id, sizeof(id), "thread-%d", i);
td[i].Init(id, i * 10, i * 20);
}
// 创建多线程
for (int i = 0; i < NUM; i++)
{
pthread_t id;
pthread_create(&id, nullptr, routine, &td[i]);
td[i].SetId(id);
}
//等待多个线程
for(int i = 0; i < NUM; i++)
{
pthread_join(td[i].Id(), nullptr);
}
//汇总处理结果
for(int i = 0; i < NUM; i++)
{
printf("td[%d]: %d+%d=%d[%ld]\n", i, td[i].A(), td[i].B(), td[i].Result(), td[i].Id());
}
// 1,新线程和main线程谁先运行,不确定
// 2,线程创建出来, 要对进程的时间片进行瓜分
// 8,传参问题: 传递参数,可以是变量,数字,对象!
// pthread_t tid1;
// ThreadData *td = new ThreadData("thread-1", 10, 20);
// pthread_create(&tid1, nullptr, routine, td);
return 0;
}
void* routine2(void* args)
{
std::string name = static_cast<const char* >(args);
while(true)
{
//3,在不加保护的情况下,显示器文件就是共享资源!
std::cout<< "我是新线程,我的名字是:" << name << ", my tid is : "<< toHex(pthread_self()) << ", 全局变量(只是检测):" << gval <<std::endl;
sleep(1);
// 6, 线程一旦出现异常, 可能会导致其他线程全部崩溃
// 6.1信号
int* p = nullptr;
*p = 100;
}
// return 0;
}
int main()
{
//
pthread_t tid1;
ThreadData *td = new ThreadData("thread-1", 10, 20);
pthread_create(&tid1, nullptr, routine2, td);
// 7,线程创建后也是 要等待和被回收的!
// 7.1 理由: a。类似僵尸进程的问题, b, 为了知道新线程的执行结果
ThreadData *rtd = nullptr;
int n = pthread_join(tid1, (void**)&rtd);//我们可以保证,执行完毕,任务一定处理完了,结果变量一定已经被写入了!
if(n != 0 )
{
std::cerr << "join error: " << n << ", " << strerror(n) << std::endl;
return 1;
}
std::cout<< "join suceess!, ret: " << rtd->Result() << std::endl;
delete td;
}
lamda
表达式构造对象也可以替换使用处理新线程方法
void* routine(void * args)
{
std::string name = static_cast<const char *>(args);
while(true)
{
std::cout<< "我是新线程 , 我的名字是: " << std::endl;
sleep(1);
}
return 0;
}
int main()
{
pthread_t tid ;
int n = pthread_create(&tid, nullptr, routine, (void*)"thread-1");
if(n != 0)
{
std::cout << "create thread error: " <<strerror(n) <<std::endl;
return 1;
}
while(true)
{
std::cout << "我是main线程..." <<std::endl;
sleep(1);
}
return 0;
}
while true; do ps -aL | head -1 && ps -aL | grep mythread ; sleep 1; done;
int main()
{
std::thread t([] () {
while(true)
{
std::cout<< "我是新线程, 我的名字 :new thread " << std::endl;
sleep(1);
}
});
while(true)
{
std::cout<< "我是main线程..." << std::endl;
sleep(1);
}
return 0;
}