线程的概念
在Linux中实际上没有真正意义上的线程,它是根据进程的模型模拟出来的一种类似于进程一样的程序,它是在进程的地址空间中存在,并且可以吧进程中的任务分开执行,达到真正意义上的并行,加快操作系统的运行速度;所以说:线程是Linux中轻量级的进程。(在Windows中实现了真正意义上的线程)
因为,线程存在于进程的地址空间中,所以,线程可以共享进程的一些资源和环境:
1、文件描述表,2、每种信号的处理方式;3、当前的工作目录;4、用户ID和组ID
但是也有自己私有的资源是各个线程所不能共享的:
1、线程ID;
2、上下文,包括各种寄存器的值、程序计数器和栈指针
3、栈空间(存储的是函数)
4、errno变量;
5、信号屏蔽字;
6、调度优先级;
所以;我们可以知道:
1、进程是分配系统资源的基本单位(进程强调独占系统资源),调度的基本单位是线程(线程强调共享系统两个资源)
2、线程在进程的地址空间中执行;
3、Linux中国没有真正意义上的线程;
因为在线程是有POSIX标准定义的,所以我们在进行编译的时候要加上-lpthread;
线程控制
创建线程
int pthread_create(pthread_t *thread, const pthread_attr_t *arr, void *(*start_rountine)(void *), void *arg);
*thread 为线程的ID,只在用户区有效
const pthread_attr_t *arr 为线程的属性,一般为NULL;
void *(*start_rountine)(void *) 为新线程的函数名
void *arg 为新线程的函数参数
在线程中,新线程何主线程的调用也是随机的,并不是系统指定先调用那个;
终止线程
1、从线程中调用return,不适合用于主线程;
2、可以调用pthread_cancel终止同一个进程中的线程;(别的线程终止指定线程)
3、线程可以调用pthread_exit终止自己
在线程终止后都有特定的返回值,会通过线程等待函数join来接受:
在return和exit函数中返回值是自己可以指定的,在取消线程pthread_cancel中返回值是特定的-1;
线程等待
int pthread_join(pthread_t thread, void **retval);
thread 线程ID
**retval为返回值它是个二级指针
对于上述终止线程中的各个函数的返回值是有线程等待函数join来接受的,并且返回终止线程的函数指定的返回值;
接受**retval的时候,定义一个一级指针,然后取地址去接受,在输出的时候直接强制转换为int型的来输出:
因为:新线程中是一个一级指针,在主线程中用一级指针的地址去接受,那么这个一级指针的值就和返回值里面的值是一样的,所以转换为int型可以直接输出;
附上全部代码:
my_pthread.c
#include<stdio.h>
#include<pthread.h>
#include<string.h>
void* thread_run()//new thread
{
while(1)
{
pthread_detach(pthread_self());
printf("new thread: pid: %lu, tid: %lu\n", getpid(), pthread_self());
// return (void*)1;//利用return终止线程
// pthread_exit((void*)2);//利用pthread_exit终止线程
}
}
int main()
{
pthread_t id;
void* val;
int ret = pthread_create(&id, NULL, thread_run, NULL);
if(ret != 0){
printf("pthread_creat error! %d\n", strerror(ret));
return ret;
}
pthread_cancel(id);//利用pthread_cancel进行终止线程
pthread_detach(id);
pthread_join(id, &val);
// printf("main thread: pid: %lu, tid: %lu\n, ", getpid(), pthread_self());
printf("main thread: pid: %lu, tid: %lu, return code: %d\n", getpid(), pthread_self(), (int)val);
sleep(1);
return 0;
}
Makefile
my_pthread:my_pthread.c
gcc -o $@ $^ -lpthread
.PHONY:clean
clean:
rm -f my_pthread
线程分离
线程可分为可结合的或者分离的,一个可结合的线程能够被其他线程回收期资源和杀死,在其他线程回收前,它的存储器资源是不释放的,一个可分离的线程是不嗯呢该被其他线程回收或杀死的,它的存储器资源在他终止时有系统自动释放。
默认创建的线程是可结合的;
可结合的线程你都应该要被显示的回收,即调用pthread_join;要么替换调用pthread_detach函数分离
如果可结合的线程结束运行没有被join,可能会导致类似与僵尸进程的状态;
在主线程永不退出的情况下:可以有两种方法来设置分离
pthread_detach(pthread_self())(线程自己设置为分离)
pthread_detach(thread_id)(由主线程将制定ID的线程设置为分离,程序运行结束后自动释放所有资源)
当分离成功后,该线程不依赖与主线程,所以主线程join接收时,就会出现等待失败的情况;
线程同步与互斥
mutex(互斥量)
mutex和前面的 semaphore(信号量)差不多,都是一把锁,用来保护临界资源在执行的时候不被别的线程所中断,所以在多线程的程序中,我们就能保护临界资源;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;(创建一个全局变量的互斥锁)
pthread_mutex_t mutex;
pthrad_once_t foo_once = PTHREAD_ONCE_INIT;
(创建一个局部变量的互斥锁)
int pthread_mutex_lock(pthrad_mutex_t *mutex);(在临界区前面加锁)
int pthread_mutex_unlock(pthread_mutex_t *mutex);(在临界区后去掉锁)
用10个线程来对临界区进行访问,并且在加锁后
#include<stdio.h>
#include<pthread.h>
#include<stdlib.h>
int g_count = 0;
//pthread_mutex_t Mutex = PTHREAD_MUTEX_INITIALIZER;//全局锁
static pthread_mutex_t Mutex;
static pthread_once_t foo_once = PTHREAD_ONCE_INIT;
void init_foo()
{
pthread_mutex_init(&Mutex, NULL);//局部锁
}
void* thread_run(void* _val)
{
pthread_once(&foo_once, init_foo);
// pthread_mutex_init(&Mutex, NULL);
int val = 0;
int i = 0;
while(i++ < 5000){
pthread_mutex_lock(&Mutex);
val = g_count;
printf("thread id : %lu, g_count: %d\n", pthread_self(), g_count);
g_count = val + 1;
pthread_mutex_unlock(&Mutex);
}
return NULL;
}
int main()
{
pthread_t id0;
pthread_t id1;
pthread_t id2;
pthread_t id3;
pthread_t id4;
pthread_t id5;
pthread_t id6;
pthread_t id7;
pthread_t id8;
pthread_t id9;
pthread_create(&id0, NULL, thread_run, NULL);
pthread_create(&id1, NULL, thread_run, NULL);
pthread_create(&id2, NULL, thread_run, NULL);
pthread_create(&id3, NULL, thread_run, NULL);
pthread_create(&id4, NULL, thread_run, NULL);
pthread_create(&id5, NULL, thread_run, NULL);
pthread_create(&id6, NULL, thread_run, NULL);
pthread_create(&id7, NULL, thread_run, NULL);
pthread_create(&id8, NULL, thread_run, NULL);
pthread_create(&id9, NULL, thread_run, NULL);
pthread_join(id0, NULL);
pthread_join(id1, NULL);
pthread_join(id2, NULL);
pthread_join(id3, NULL);
pthread_join(id4, NULL);
pthread_join(id5, NULL);
pthread_join(id6, NULL);
pthread_join(id7, NULL);
pthread_join(id8, NULL);
pthread_join(id9, NULL);
printf("main g_count: %d\n", g_count);
return 0;
}