嵌入式学习第二十一天--线程
线程
进程的创建:
1. fork --- 复制
2. vfork 虚拟拷贝
copy on write //写时拷贝
总结:
1.进程创建过程 --- 是通过复制
导致 创建时 ,需要拷贝大量的数据 --- 影响效率
2.线程拷贝
减少了拷贝的数据量 ---提升创建效率
3.进程 是 CPU 调度的基本单位
进程(重量级的进程): 更多侧重于 成为 资源分配的单位 ---- 资源分配的基本单位
线程(轻量级的进程): 更侧重于 成为一个 执行单位 ---- 调度执行的最小单位
线程组成:
线程id //long -- 8字节
程序计数器 (program counter) //寄存器 --- 8字节
其它寄存器 // 51 * 8 字节 = 408字节
栈 //8M
进程的组成:
text|data|bss|堆栈| + pcb
线程 --- 主要侧重 去 执行任务 (资源更多的是共享了进程资源)
线程 和 进程之间的关系:
1.线程是依附于进程的
2.进程不存在,相关的线程也不复存在
3.一个进程中,可以创建多个线程
4.进一步提高了并发程度
fork
|
/ \
father child
/ \
多个线程 多个线程
linux下如何进行线程编程:
NPTL 函数库 --- 提供了线程的操作
New Posix Thread Library //新的posix标准的线程库
Native //本地线程库
NPTL库 --- stdio
1.创建
2.执行
3.结束
pthread_create //线程创建
#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
功能:
创建一个新的线程
参数:
@thread --- 线程id
@attr --- 线程属性 //默认属性 NULL --可结合性
//可分离属性
@start_routine ---线程执行函数//线程回调函数 --- 提现线程任务执行的部分
@arg ---这是传给 start_routine 函数的参数
void * do_something(void *arg) //线程执行函数
{
}
返回值:
成功 0
失败 返回错误码
创建出来的线程 --- 次线程 子线程
原先main函数对应执行流 --- 主线程
练习:
创建两个线程
线程1 打印 线程1 ... pid //getpid
线程2 打印 线程2 ... pid //getpid
注:
编译
gcc pthread_creat.c -lpthread
//链接库
[NPTL 库] <--- tid表示的是在 NPTL库中 对线程的 一个代表
|
-----------|------------------- getpid
内核 |
|
do_clone(,SHARED) //线程 --- 进程
eg:
定义一个全局变量
要求 线程1 做 加 1
线程2 加 2
线程3 加 3
#include<stdio.h>
#include<pthread.h>
#include<errno.h>
#include<unistd.h>
//int num;
void * do_something1(void *arg)
{
int *n=(int *)arg;
while(1)
{
(*n)+=1;
printf("num = %d\n",*n);
sleep(2);
}
return NULL;
}
void * do_something2(void *arg)
{
int *n=(int *)arg;
while(1)
{
(*n)+=2;
printf("num = %d\n",*n);
sleep(2);
}
return NULL;
}
void * do_something3(void *arg)
{
int *n=(int *)arg;
while(1)
{
(*n)+=3;
printf("num = %d\n",*n);
sleep(2);
}
return NULL;
}
typedef void *(*thread_cb_t)(void*);
int main(int argc, const char *argv[])
{
thread_cb_t func[3]={do_something1,do_something2,do_something3};;
pthread_t tid[3];
int i=0;
int m=0;
for(i=0;i<3;i++)
{
int ret;
if((ret=pthread_create(&tid[i],NULL,func[i],&m))!=0)
{
errno=ret;
perror("pthread_create fail");
return -1;
}
}
while(1)
{
printf("--------main------m=%d\n",m);
sleep(2);
}
return 0;
}
好处:
线程共享数据方便
--- 带来 ---竞争问题
线程结束方式:
The new thread terminates in one of the following ways:
* It calls pthread_exit(3),
specifying an exit status value that is available to another thread
in the same process that calls pthread_join(3).
* It returns from start_routine().
This is equivalent to calling pthread_exit(3) with the value supplied in the return statement.
* It is canceled (see pthread_cancel(3)).
* Any of the threads in the process calls exit(3), or the main thread performs a return from main().
This causes the termination of all threads in the process.
pthread_exit
void pthread_exit(void *retval);
功能:
结束线程
参数:
retval --- 带出的值的 地址
注意:
pthread_exit //带出的是 保存了 退出状态值 的空间的地址
//退出状态值 不能放在栈上
int pthread_join(pthread_t thread, void **retval);
int pthread_cancel(pthread_t thread)
功能:
取消线程
参数:
thread --- 要取消的线程的tid
返回值
成功 0
失败 错误码
注意:
1. 线程间 的地位是平等的 可以相互取消
总结:
线程:
1.线程 --- 轻量级的进程
2.线程组成
线程tid
程序计数器
其它寄存器
栈
3.创建线程
pthread_create
tid
属性 -- 可结合 可分离 ---决定最终资源的回收方式
线程执行函数 --- 这是体现线程任务的部分
arg -- 传给线程执行函数的参数
4.线程退出
a.pthread_exit
b.return ---线程执行函数中
c.pthread_cancel
d.exit
5.线程退出状态值
pthread_join
eg:
多线程拷贝文件
#include <stdio.h>
#include <pthread.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/stat.h>
#include <unistd.h>
typedef struct
{
int fd_s;
int fd_d;
int size;
int len;
int id;
}msg_t;
#if 0
void * do_copy (void *arg)
{
msg_t *p = arg;
lseek(p->fd_s,p->size*p->id,SEEK_SET);
lseek(p->fd_d,p->size*p->id,SEEK_SET);
printf("tid = %ld id = %d fd_s = %d fd_d = %d size = %d len = %d\n",pthread_self(),p->id,p->fd_s,p->fd_d,p->size,p->len);
char buf[p->len];
int ret = read(p->fd_s,buf,p->len);
write(p->fd_d,buf,ret);
return NULL;
}
#endif
void * do_copy (void *arg)
{
msg_t p = *(msg_t*)arg;
lseek(p.fd_s,p.size*p.id,SEEK_SET);
lseek(p.fd_d,p.size*p.id,SEEK_SET);
printf("tid = %ld id = %d fd_s = %d fd_d = %d size = %d len = %d\n",pthread_self(),p.id,p.fd_s,p.fd_d,p.size,p.len);
char buf[p.len];
int ret = read(p.fd_s,buf,p.len);
write(p.fd_d,buf,ret);
return NULL;
}
//cp src dest
int main(int argc, const char *argv[])
{
if (argc!=3)
{
printf("Usage: %s <src> <dest>\n",argv[0]);
return -1;
}
int fd_s = open(argv[1],O_RDONLY);
int fd_d = open(argv[2],O_WRONLY|O_TRUNC|O_CREAT,0666);
if (fd_s < 0 || fd_d < 0)
{
perror("open fail");
return -1;
}
int n = 0;
printf("Input threads num: ");
scanf("%d",&n);
int i = 0;
int ret = 0;
pthread_t tid[n];
msg_t msg[n];
struct stat st;
if (stat(argv[1],&st) < 0)
{
perror("stat fail");
return -1;
}
int f_len = st.st_size;
for (i = 0; i < n; ++i)
{
// msg.fd_s = fd_s;
// msg.fd_d = fd_d;
// msg.size = f_len / n;
// msg.id = i;
msg[i].fd_s = fd_s;
msg[i].fd_d = fd_d;
msg[i].size = f_len / n;
msg[i].id = i;
if (i == n-1)
{
msg[i].len = f_len - (f_len/n)*(n-1);
}else
{
msg[i].len = f_len/n;
}
ret = pthread_create(&tid[i],NULL,do_copy,&msg[i]);
if (ret != 0)
{
errno = ret;
perror("pthread_create fail");
return -1;
}
// sleep(1);
}
printf("----main-----\n");
for (i = 0; i < n; ++i)
pthread_join(tid[i],NULL);
close(fd_s);
close(fd_d);
return 0;
}
线程的属性的设置:
1. pthread_detach //将指定的线程设置为分离状态 --- 那么资源的回收将自动完成
2. pthread_attr_setdetachstate
int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
功能:
设置线程的属性
参数:
@attr --- 属性的对象 (变量)
@detachstate --- 要设置的属性
PTHREAD_CREATE_DETACHED //分离
PTHREAD_CREATE_JOINABLE //结合
返回值
成功 0
失败 errno
//1.初始化 一个 属性的对象
int pthread_attr_init(pthread_attr_t *attr);
//2.设置属性信息
int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
//3.销毁
int pthread_attr_destroy(pthread_attr_t *attr);
atexit()
atexit(); //注册退出清理函数
//进程结束
//return //main
//exit
void pthread_cleanup_push(void (*routine)(void *), void *arg);
//线程退出清理函数:
功能:注册一个线程清理函数
参数,routine,线程清理函数的入口
arg,清理函数的参数。
返回值,无
void pthread_cleanup_pop(int execute);
功能:调用清理函数
execute,
非0 执行清理函数
0 ,不执行清理
返回值,无
注意:
1. 使用时 pthread_cleanup_push pthread_cleanup_pop 要一起用 ,需要在一个代码块
触发方式:
1.pthread_cleanup_pop(非零值)
2.pthread_cleanup_pop(0) //pthread_exit() //退出动作会导致 触发
pthread_exit(NULL);
pthread_cleanup_pop(0);
3.pthread_cancel(); //被其它线程结束时
eg:
定义要给全局变量
int cnt = 0;
创建两个线程 --- 执行50000
每个线程
int temp = cnt;
printf("cnt = %d\n",cnt);
temp = temp + 1;
cnt = temp;
#include<stdio.h>
#include<errno.h>
#include<pthread.h>
int cnt =0;
pthread_mutex_t mutex;
void*dosomething1(void*arg)
{
int n=50000;
while(n)
{ pthread_mutex_lock(&mutex);
int temp =cnt;
printf("cnt = %d\n",cnt);
temp = temp+1;
cnt = temp;
pthread_mutex_unlock(&mutex);
--n;
}
return NULL;
}
void*dosomething2(void*arg)
{
int n=50000;
while(n)
{
pthread_mutex_lock(&mutex);
int temp =cnt;
printf("cnt = %d\n",cnt);
temp = temp+1;
cnt = temp;
pthread_mutex_unlock(&mutex);
--n;
}
return NULL;
}
typedef void*(*pthread_cb_t)(void *);
int main(int argc, const char *argv[])
{
pthread_t pid[2];
int i=0;
int ret;
pthread_cb_t func[2]={dosomething1,dosomething2};
for(i=0;i<2;i++)
{
ret = pthread_create(&pid[i],NULL,func[i],NULL);
if(ret!=0)
{
errno = ret;
perror("pthread_create fail");
return -1;
}
}
printf("-----------main-----------\n");
pthread_join(pid[0],NULL);
pthread_join(pid[1],NULL);
return 0;
}
线程 对比 进程
进程
优点
进程空间独立 --- 更稳定 安全 可靠
缺点
进程创建,调度效率低
数据共享不方便 ---- 进程间通信
线程
优点
创建,调度效率高
共享数据方便
缺点
共享进程空间和资源
---不稳定,不安全
共享数据 ---资源的竞争
线程进阶:
全局的cnt ---- 共享资源(公共资源) --- 临界资源
临界区 --- 访问临界资源代码段
互斥操作
互斥 --- 排他性
原子性操作 --- 不可再分的操作
临界资源: 共享资源
临界区 : 一段代码区域(访问临界资源的那段代码)
原子操作: 要么不操作,要操作,一定是一次完整的操作。不能被打断。
概念:互斥 ===》在多线程中对临界资源的排他性访问。
互斥机制 ===》互斥锁 ===》保证临界资源的访问控制。
pthread_mutex_t mutex;
互斥锁类型 互斥锁变量 内核对象
框架:
定义互斥锁 ==》初始化锁 ==》加锁 ==》解锁 ==》销毁
**** *** ***
//互斥锁 互斥量
pthread_mutex_init(); //初始化一把锁
pthread_mutex_lock(); //上锁
pthread_mutex_unlock(); //解锁
pthread_mutex_destroy();//销毁一把锁
1、定义:
pthread_mutex_t mutex;
2、初始化锁
int pthread_mutex_init(
pthread_mutex_t *mutex,
const pthread_mutexattr_t *attr);
功能:将已经定义好的互斥锁初始化。
参数:mutex 要初始化的互斥锁
atrr 初始化的值,
一般是NULL表示默认锁 //一般的锁 ,读写锁
返回值:成功 0
失败 非零
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; //静态初始化
3、加锁:
int pthread_mutex_lock(pthread_mutex_t *mutex);
功能:用指定的互斥锁开始加锁代码
加锁后的代码到解锁部分的代码属于原子操作,
在加锁期间其他进程/线程都不能操作该部分代码
如果该函数在执行的时候,mutex已经被其他部分
使用则代码阻塞。
参数: mutex 用来给代码加锁的互斥锁
返回值:成功 0
失败 非零
4、解锁
int pthread_mutex_unlock(pthread_mutex_t *mutex);
功能:将指定的互斥锁解锁。
解锁之后代码不再排他访问,一般加锁解锁同时出现。
参数:用来解锁的互斥锁
返回值:成功 0
失败 非零
5、销毁
int pthread_mutex_destroy(pthread_mutex_t *mutex);
功能:使用互斥锁完毕后需要销毁互斥锁
参数:mutex 要销毁的互斥锁
返回值:成功 0
失败 非零
注意:
原则 --- 锁的区域 尽可能小
eg:
创建两个线程
线程1 打印 hello
线程2 打印 world
#include <stdio.h>
#include <pthread.h>
#include <errno.h>
void* print_hello(void *arg)
{
while (1)
{
printf("hello ");
}
}
void* print_world(void *arg)
{
while (1)
{
printf("world\n");
}
}
int main(int argc, const char *argv[])
{
pthread_t tid[2];
int ret = 0;
if ((ret = pthread_create(&tid[0],NULL,print_hello,NULL)) != 0)
{
errno = ret;
perror("pthread_create fail");
return -1;
}
if ((ret = pthread_create(&tid[1],NULL,print_world,NULL)) != 0)
{
errno = ret;
perror("pthread_create fail");
return -1;
}
printf("--main--\n");
pthread_join(tid[0],NULL);
pthread_join(tid[1],NULL);
return 0;
}