1.线程和进程启动终止方式
1.创建子进程,子线程
2.退出进程/线程
3.回收僵尸进程资源 / 回收线程资源
4.进程终止函数 / 线程清理函数
2.线程状态切换
pthread_create()变为就绪态,调度后成为运行态,pthread_join()成为阻塞态,从运行态运行完或者异常退出后进入线程结束
3.线程属性初始化和销毁
最重要的是线程分离状态属性,其他系统会根据调度自己设置
使用线程属性前,需要初始化,结束再销毁
1.设置和获得分离属性
1.detach(分离) 第一个函数是获得当前线程属性中的分离属性,第二个是设置线程的分离状态属性
2. detachstate取值为PTHREAD_CREATE_JOINABLE 要在主线程调用pthread_join()系统资源才会释放
3.以分离状态启动的线程,在线程结束后会自动释放占有的系统资源
4.线程启动分离属性编程实战
pthread_join(id, &)用于释放线程结束前相关资源
1.以正常状态启动需要调用pthread_join()
2.以分离状态启动线程不要调用pthread_join(),会出错,也不能利用这个函数获取子线程运行函数返回结果,分离的意思是主控线程于子线程分离开了
3.分离属性在网络通讯中使用的多,例如服务器要响应几百个客户端请求,启动很多个进程不合适,在服务器端启动多线程来处理服务器端的大并发客户端请求,来一个处理一个,处理完了自动释放资源。在服务器端如何如理客户端多并发处理请求,启动线程以分离状态启动,这是最优的方式
5.线程前章节回顾
6.线程同步和互斥
1.线程互斥
在全局变量使用,例如存款,一个线程存钱100万,还没存入被另外一个线程将存款金额修改为一万,这样导致存入只有一万,这样就涉及线程不安全,一个线程对共享资源操作时,其他线程就等待,其他线程按照系统优先级调度对共享资源进行访问和操作,这样是互斥的操作,线程的互斥,线程的执行是相互排斥的
利用以下技术达到线程互斥
2.线程同步
建立在线程互斥基础上,但也要考虑线程先后执行约束问题,后面线程必须等待前一个线程操作完毕,结果出来后,后面线程才能对这个结果进行操作
研发小组进行软件开发,测试小组对软件项目进行测试,类似两个线程,研发小组对软件开发好后,再对软件进行测试,这里包含线程互斥,包含线程相互执行的约束,等到研发小组对软件开发好后,测试小组再对共享资源进行操作,测试小组严重依赖与研发小组
3.线程互斥案例(银行ATM操作)
堆中
3.线程互斥锁(互斥量)
多个线程共享资源的场景使用,电话亭,进入电话亭打电话锁住(打完后开门其他人根据先后再进入) 同一时刻只能有一个线程拿到该锁 一个线程对共享资源加锁,其他线程视图上锁已经被锁的资源,将被挂起,直到拿到锁的线程解锁
互斥锁数据类型pthread_muxtex_t
互斥锁的创建和销毁
1.互斥锁初始化函数第二个参数,传入NULL,以默认的情况下创建默认互斥锁,是个标准互斥锁,所以一般情况下传入NULL就行
互斥锁上锁和解锁
pthread_mutex_lock()上锁,拿不到锁阻塞
pthread_mutex_trylock(),上锁,拿不到锁返回错误
对共享资源成功上锁到释放锁之间的区域叫做临界区
结构体中定义互斥锁与银行账户绑定
typedef struct{
int code; //账户账号
double balance;
//定义一把互斥锁
//银行账户(共享资源),互斥锁
/*
建议互斥锁用来锁定一个账户,和账户绑定在一起,
尽量不设置成全局变量,否则
可能出现一把锁去锁几百个账户,导致并发性能降低
*/
pthread_mutex_t mutex;
}Account;
account.h文件代码
#ifndef __ACCOUNT_H__
#define __ACCOUNT_H__
#include <pthread.h>
typedef struct{
int code; //账户账号
double balance;
//定义一把互斥锁
//银行账户(共享资源),互斥锁
/*
建议互斥锁用来锁定一个账户,和账户绑定在一起,
尽量不设置成全局变量,否则
可能出现一把锁去锁几百个账户,导致并发性能降低
*/
pthread_mutex_t mutex;
}Account;
//创建账户
extern Account* create_account(int code, double balance);
//销毁账户
extern void destroy_account(Account *a);
//取款
extern double withdraw(Account *a, double amt);
//存款
extern double deposit(Account *a, double amt);
//查看账户余额
extern double get_balance(Account *a);
#endif
account.c代码
#include "account.h"
#include <malloc.h>
#include <assert.h>
#include <string.h>
#include <unistd.h>
//创建账户
Account* create_account(int code, double balance)
{
//创建在堆中
Account *a = (Account*)malloc(sizeof(Account));
assert(a != NULL); //()里面为bool类型,为假程序会就此结束
a->code = code;
a->balance = balance;
//对互斥锁进行初始化
pthread_mutex_init(&a->mutex, NULL);
return a;
}
//销毁账户
void destroy_account(Account *a)
{
assert(a != NULL);
//销毁互斥锁
pthread_mutex_destroy(&a->mutex);
free(a);
}
//取钱
double withdraw(Account *a, double amt)
{
assert(a != NULL);
//从成功上锁到释放锁叫做临界区
//加锁,对共享资源(账户)进行加锁,资源被占用则会阻塞
pthread_mutex_lock(&a->mutex);
if(amt < 0 || amt > a->balance){
pthread_mutex_unlock(&a->mutex);
return 0.0;
}
double balance = a->balance;
//sleep(1)用来使这个线程延时让其他线程占用CPU取款,模拟情景
sleep(1); //取款操作有个过程,停顿一下,存取数据,读取数据
balance -= amt;
a->balance = balance; //银行账户余额
//释放互斥锁
pthread_mutex_unlock(&a->mutex);
return amt; //取款成功返回取款金额
}
//存钱
double deposit(Account *a, double amt)
{
assert(a != NULL);
//上锁
pthread_mutex_lock(&a->mutex);
if(amt < 0){
//解锁
pthread_mutex_unlock(&a->mutex);
return 0.0;
}
a->balance += amt;
sleep(1);
//解锁
pthread_mutex_unlock(&a->mutex);
return amt;
}
//查看账户余额
double get_balance(Account *a)
{
assert(a != NULL);
//上锁
pthread_mutex_lock(&a->mutex);
//解锁
pthread_mutex_unlock(&a->mutex);
return (a->balance);
}
account_test.c代码
#include "account.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
typedef struct{
char name[20]; //名字
Account *account; //账户
double amt; //存取款金额
}OperArg; //操作参数
//定义取款操作的线程运行函数
void* withdraw_fn(void *arg)
{
OperArg *oa = (OperArg*)arg;
//取钱的账户和金额,取款失败返回0
double amt = withdraw(oa->account, oa->amt);
printf("%s(0x%lx) withdraw %f from account %d\n",
oa->name, pthread_self(),
amt,
oa->account->code);
return (void*)0;
}
//定义存款操作的线程运行函数
void* deposit_fn(void *arg)
{
OperArg *oa = (OperArg*)arg;
//存钱的账户和金额
double amt = deposit(oa->account, oa->amt);
printf("%s(0x%lx) withdraw %f from account %d\n",
oa->name, pthread_self(),
amt,
oa->account->code);
return (void*)0;
}
//定义检查银行账户的线程运行函数
void* check_fn(void *arg)
{
OperArg* oa = (OperArg*)arg;
double balance;
balance = get_balance(oa->account);
printf("balance %f", balance);
return (void*)0;
}
int main(void)
{
int err;
pthread_t boy, girl;
//第一个是账户编码号,第二个是余额
//这里两个线程的账户都初始化为a
Account *a = create_account(100001, 10000);
OperArg o1, o2; //传入不同线程运行函数中
strcpy(o1.name, "boy");
o1.account = a;
o1.amt = 10000;
strcpy(o2.name, "girl");
o2.account = a;
o2.amt = 10000;
//启动两个子线程(boy 和girl线程)
//同时去操作同一个银行账户
if((err = pthread_create(&boy, NULL, withdraw_fn, (void*)&o1)) != 0)
{
perror("pthread create error");
}
if((err = pthread_create(&girl, NULL, withdraw_fn, (void*)&o2)) != 0)
{
perror("pthread create error");
}
pthread_join(boy, NULL);
pthread_join(girl, NULL);
//
printf("account balance: %f\n", get_balance(a));
destroy_account(a); //销毁账户
return 0;
}
gcc运行编译结果
ji@ji-VM:~/c/pthread/account$ gcc account.c account_test.c
ji@ji-VM:~/c/pthread/account$ ./a.out
boy(0x7a8183200640) withdraw 10000.000000 from account 100001
girl(0x7a8182800640) withdraw 0.000000 from account 100001
account balance: 0.000000
ji@ji-VM:~/c/pthread/account$
4.互斥锁属性创建和销毁
互斥锁属性创建传入NULL则是创建标准互斥锁
5.设置和获取互斥锁进程共享属性
7.互斥锁类型操作
互斥锁初始化的时候
lockattr.c的代码,验证多次上锁同一互斥锁时,各种互斥锁属性对应的反应
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[])
{
//定义互斥锁
pthread_mutex_t mutex;
if(argc < 2){
printf("-usage:%s [error|normal|recursive]\n",
argv[0]);
exit(1);
}
//定义互斥锁属性
pthread_mutexattr_t mutexattr;
//初始化互斥锁属性
pthread_mutexattr_init(&mutexattr);
//字符串比较函数strcmp(),字符串相等返回0
if(!strcmp(argv[1], "error")){
//设置互斥锁类型,为检错互斥锁
pthread_mutexattr_settype(&mutexattr,
PTHREAD_MUTEX_ERRORCHECK);
}else if(!strcmp(argv[1], "normal")){
//设置标准互斥锁
pthread_mutexattr_settype(&mutexattr,
PTHREAD_MUTEX_NORMAL);
}else if(!strcmp(argv[1], "recursive")){
//设置递归互斥锁
pthread_mutexattr_settype(&mutexattr,
PTHREAD_MUTEX_RECURSIVE);
}
//初始化互斥锁
pthread_mutex_init(&mutex, &mutexattr);
//上锁
if(pthread_mutex_lock(&mutex) != 0){
printf("lock failure\n");
}else{
printf("lock success\n");
}
//上锁第二次,用于模拟第二次
//上锁面对不同互斥锁属性
if(pthread_mutex_lock(&mutex) != 0){
printf("lock failure\n");
}else{
printf("lock success\n");
}
//释放锁
pthread_mutex_unlock(&mutex);
return 0;
}
运行结果,正常锁第二次调用会阻塞,
8.线程互斥——读写锁
1.线程使用互斥锁缺乏读并发性
2.读写锁创建和销毁
3.读写锁加锁和解锁
pthread_rwlock_rdlock() //加读锁
pthread_rwlock_wrlock()//加写锁
rwlock.c上读锁,写锁多次案例
1.读和读不排斥;
2.写和写排斥;
3.读和写排斥;
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
//定义读写锁
pthread_rwlock_t rwlock;
int main(int argc, char *argv[])
{
if(argc < 3){
printf("-usage:%s [r|w] [r|w]\n", argv[0]);
exit(1);
}
//读写锁初始化
pthread_rwlock_init(&rwlock, NULL);
if(!strcmp(argv[1], "r")){
//加读锁
if(pthread_rwlock_rdlock(&rwlock) != 0){
printf("first read lock failure\n");
}else{
printf("first read lock success\n");
}
}else if(!strcmp(argv[1], "w")){
//加写锁
if(pthread_rwlock_wrlock(&rwlock) != 0){
printf("first write lock failure\n");
}else{
printf("first write lock success\n");
}
}
if(!strcmp(argv[2], "r")){
//加读锁
if(pthread_rwlock_rdlock(&rwlock) != 0){
printf("second read lock failure\n");
}else{
printf("second read lock success\n");
}
}else if(!strcmp(argv[2], "w")){
//加写锁
if(pthread_rwlock_wrlock(&rwlock) != 0){
printf("second write lock failure\n");
}else{
printf("second write lock success\n");
}
}
//解锁,释放读写锁
pthread_rwlock_unlock(&rwlock);
pthread_rwlock_unlock(&rwlock);
//摧毁读写锁
pthread_rwlock_destroy(&rwlock);
return 0;
}
用读写锁解决银行取钱和存钱和查看余额之间的互斥利害