网站建设典型发言黄页88
线程
进程的创建:
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;
}
#endifvoid * 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;
}