欢迎光临
我们一直在努力

linux线程

守护进程

1.特点

  • 后台服务进程
  • 独立于终端
  • 不受用户登录注销影响

2.进程组

  • 进程组的第一个进程是进程组的组长
  • 进程组的id是组长的id

3.会话

  • 进程组的组长不能创建会话
  • 但创建会话的进程可以成为进程组的组长

获取进程所属的会话ID
pid_t getsid(pid_t pid)
创建一个会话
pid_t setid(void)

一般流程
fork()子进程后,父进程退出(父进程是进程组的组长不能创建会话)
子进程创建会话

线程概念

主线程和子线程
共享:

  • .text
  • .bss
  • .data
  • 动态加载区
  • 环境变量
  • 命令行参数
  • 通信:全局变量,堆
    不共享
    一共五个线程,栈区被平均分成五块

线程的创建

1.创建线程‐‐pthread_create

int pthread_create( pthread_t *thread), //线程ID = 无符号长整型
const pthread_attr_t *attr, //线程属性,NULL
void *(*start_routine)(void *), //线程处理函数
void *arg); //线程处理函数

2.单个线程退出 –pthread_exit

void pthread‐exit(void *retval);
retval指针:必须指向全局,堆

3.阻塞等待线程退出,获取线程退出状态–pthread_join

int pthread_join(pthread_t pthread, void *retval)
参数:
pthread:要回收的子线程的ID
retval:读取线程退出的携带信息
传出参数
void ptr;
pthread_join(pthid,&ptr);
指向的内存和pthread_exit参数指向地址一致

4.线程分离–pthread_detach

函数原型:int pthread_datach(pthread_t thread);
调用该函数之后不需要 pthread_join
子线程会自动回收自己的PCB

5…杀死(取消)线程–pthread_cancel

int pthread_cancel(pthread_t pthread);
使用注意事项:
在要杀死的子线程对应的处理的函数的内部,必须做过一次系统调用
write read printf
int a = 2; int b = a+3;
pthread_testcancel();设置取消点

线程的分离属性

1.线程分离属性:pthread_attr_t attr
2.对线程属性操作的函数

  • 对线程属性进行初始化

int pthread_attr_init(pthread_attr_t* attr)

  • 设置线程分离属性

int pthread_attr_setdetachstate(
pthread_attr_t* attr,
int detachstate
);
参数:
attr : 线程属性
detachstate
PTHREAD_CREATE_DETACHED(分离)
PTHREAD_CREATE_JOINABLE(非分离)

  • 释放线程资源函数

int pthread_attr_destroy(pthread_attr_t* attr)

互斥量

互斥锁使用的步骤

1.创建互斥锁 pthread_mutex_t mutex;
2.初始化互斥锁 pthread_mutex_init(&mutex,NULL);
3.对进程同步的共享资源加锁

  • 加锁 pthread_mutex_lock(&mutex); //加锁后未获得临界资源的线程会被阻塞
  • 进程同步的共享资源
  • 解锁 pthread_mutex_unlock(&mutex);
    4.销毁互斥锁 thread_mutex_destory(&mutex);

特点:线程串行完成,效率低,但为了代码有效性不得不牺牲效率

原子操作

  • 互斥锁内部的核心实现是原子操作,它包含了检查互斥锁的状态(空现或忙碌)并上锁,需一气呵成执行完成
  • 但 pthread_mutex_lock涉及了系统调用(让出cpu)与内存屏障,检查互斥锁的状态(空现或忙碌)并上锁,并不是一次合成的,不是原子操作

死锁

造成死锁的原因
1.自己锁自己
上锁后未解锁

pthread_mutex_lock(&mutex);
pthread_mutex_lock(&mutex);
进程同步的共享资源
pthread_mutex_unlock(&mutex);

2.多个进程争抢共享资源且执行孙旭所需不当,线程之间循环等待资源,造成死锁

在这里插入图片描述

读写锁

特点

  • 读写不可同时进行
  • 读共享
  • 写独占
  • 写的优先级高

场景

  • 线程A加写锁成功,线程B请求读锁
    • 线程B阻塞
  • 线程A持有读锁,线程B请求写锁
    • 线程B阻塞
  • 线程A拥有读写,线程B请求读锁
    • 线程B加锁
  • 线程A持有读锁,然后线程B请求写锁,然后线程C请求读锁
    • 线程B阻塞,线程C阻塞
    • 线程B加锁,线程C阻塞
    • 线程C加锁
  • 线程A持有写锁,然后线程B请求读锁,然后线程C请求写锁
    • 线程B阻塞,线程C阻塞
    • 线程C加锁, 线程B阻塞
    • 线程B加锁

相关函数

  • 初始化读写锁
    • pthread_rwlock_init(pthread_rwlock_t* restrict rwlock,
      const pthread_rwlockattr_t* restrict attr );
  • 销毁读写锁
    • pthread_rwlock_destroy(pthread_rwlock_t* rwlock):
  • 加读锁
    • pthread_rwlock_rdlock(pthread_rwlock_t* rdlock);
      阻塞:之前对这把锁加的是写锁的操作
  • 尝试加读锁
    • pthread_rwlock_tryrdlock(pthread_rwlock_t* rwlock);
      加锁成功:返回0
      失败:返回错误号
  • 加写锁
    • pthread_rwlock_wrlock(pthread_rwlock_t* rwlock);
      阻塞:上一次加写锁还没解锁
      阻塞:上一次加读锁还没解锁
  • 尝试加写锁
    • pthread_rwlock_trywrlock(pthread_rwlock_t* rwlock);
  • 解锁
    • pthread_rwlock_unlock(pthread_rwlock_t* rwlock)

条件变量

条件变量不是锁,但它能阻塞线程
不满足条件时阻塞,满足条件时通知阻塞的线程开始工作

#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
typedef struct node{
int data;
struct node *next;
}Node;
Node *head = NULL;
//create mutex
pthread_mutex_t mutex;
//create condition
pthread_cond_t con;
void *producer(void *arg){
while(1){
sleep(1);
Node *pnew = (Node *)malloc(sizeof(Node));
pnew->data = rand()%100;
pthread_mutex_lock(&mutex);
pnew->next = head;
head = pnew;
pthread_mutex_unlock(&mutex);
pthread_cond_signal(&con);
}
}
void *consumer(void *arg){
while(1){
pthread_mutex_lock(&mutex);
while(head == NULL){
pthread_cond_wait(&con,&mutex);
}
Node *pdel = head;
head = head->next;
printf("consumer id:%ld,del num:%d\\n",pthread_self(),pdel->data);
free(pdel);
pthread_mutex_unlock(&mutex);
}
}
int main(){

pthread_t p[8];
pthread_mutex_init(&mutex,NULL);
pthread_cond_init(&con,NULL);

for(int i = 0;i<3;++i){
pthread_create(&p[i],NULL,producer,NULL);
}
for(int i = 3;i<8;++i){
pthread_create(&p[i],NULL,consumer,NULL);
}

for(int i = 0;i<8;++i){
pthread_join(p[i],NULL);
}

pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&con);
}

在这里插入图片描述

#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
typedef struct node{
int data;
struct node *next;
}Node;
Node *head = NULL;
//create mutex
pthread_mutex_t mutex;
//create condition
pthread_cond_t con;
void *producer(void *arg){
while(1){
sleep(1);
Node *pnew = (Node *)malloc(sizeof(Node));
pnew->data = rand()%100;
pthread_mutex_lock(&mutex);
pnew->next = head;
head = pnew;
pthread_mutex_unlock(&mutex);
pthread_cond_signal(&con);
}
}
void *consumer(void *arg){
while(1){
pthread_mutex_lock(&mutex);
while(head == NULL){
pthread_cond_wait(&con,&mutex);
}
Node *pdel = head;
head = head->next;
printf("consumer id:%ld,del num:%d\\n",pthread_self(),pdel->data);
free(pdel);
pthread_mutex_unlock(&mutex);
}
}
int main(){

pthread_t p[8];
pthread_mutex_init(&mutex,NULL);
pthread_cond_init(&con,NULL);

for(int i = 0;i<3;++i){
pthread_create(&p[i],NULL,producer,NULL);
}
for(int i = 3;i<8;++i){
pthread_create(&p[i],NULL,consumer,NULL);
}

for(int i = 0;i<8;++i){
pthread_join(p[i],NULL);
}

pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&con);
}

是否会造成死锁:
不会造成死锁, pthread_cond_wait(&con,&mutex);会进行一个原子操作(首先将互斥锁mutex打开,接着线程会被阻塞在cond条件变量上),生产者生产完产品后pthread_cond_signal(&con);,消费者会尝试重新获取锁,完成消费过程

信号量

#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
#include <semaphore.h>

typedef struct node{
int data;
struct node *next;
}Node;
Node *head = NULL;
//create mutex
pthread_mutex_t mutex;
//create semaphore
sem_t p;
sem_t c;
void *producer(void *arg){
while(1){

Node *pnew = (Node *)malloc(sizeof(Node));
pnew->data = rand()%100;
sem_wait(&p);//对p信号量进行–操作,若–后小于0则阻塞线程,等待资源
pthread_mutex_lock(&mutex);
pnew->next = head;
head = pnew;
pthread_mutex_unlock(&mutex);
sem_post(&c);//对c信号量进行++操作

}
}
void *consumer(void *arg){
while(1){

sem_wait(&c);对c信号量进行操作,若后小于0则阻塞线程,等待资源
pthread_mutex_lock(&mutex);
Node *pdel = head;
head = head->next;
printf("consumer id:%ld,del num:%d\\n",pthread_self(),pdel->data);
free(pdel);
pthread_mutex_unlock(&mutex);
sem_post(&p);//对c信号量进行++操作
sleep(1);

}
}
int main(){

pthread_t p[8];
pthread_mutex_init(&mutex,NULL);
sem_init(&p,0,3);//线程同步的信号量,初始值为3
sem_init(&c,0,0);

for(int i = 0;i<3;++i){
pthread_create(&p[i],NULL,producer,NULL);
}
for(int i = 3;i<8;++i){
pthread_create(&p[i],NULL,consumer,NULL);
}

for(int i = 0;i<8;++i){
pthread_join(p[i],NULL);
}

pthread_mutex_destroy(&mutex);

}

使用信号量的原因在于线程访问共享资源是互斥的,频繁访问互斥区会降低线程同步的效率,所以加上信号量可以加快线程同步的效率

赞(0)
未经允许不得转载:171主机测评 » linux线程
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址