重生之我真不想学Linux-线程

线程

一个进程中的多个线程共享以下资源:

  • 可执行的指令
  • 静态数据
  • 进程中打开的文件描述符
  • 当前的工作目录
  • 用户ID
  • 用户组ID

每个线程中的私有资源包括:

  • 线程ID(TID)
  • PC(程序计数器)和相关寄存器
  • 堆栈
  • 错误号(errno)
  • 优先级
  • 执行状态和属性

创建线程

成功时返回0,失败时返回错误码,看的不太懂?直接上例子

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

int *testThread(char *arg) {
    printf("This is the thread test!\n");
    return NULL;
}

int main() {
    pthread_t tid;
    int rel;
    rel = pthread_create(&tid, NULL, (void*)testThread, NULL);
    printf("This is the main thread!\n");
    sleep(1);
    return 0;
}

注意:主进程的退出,创建的线程也会退出,线程创建需要事件,如果主进程马上退出,那线程不能得到执行

线程结束

void pthread_exit(void *retval);

结束当前进程
retval可被其他线程通过pthread_join获取
线程私有资源被释放

线程查看TID

第一种方法:

pthread_t pthread_self(void)

第二种方法:

通过pthread_create函数中的第一个参数。

线程的参数传递

例如上个代码:

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

int *testThread(char *arg) {
    printf("This is the thread test!\n");
    return NULL;
}

int main() {
    pthread_t tid;
    int rel;
    rel = pthread_create(&tid, NULL, (void*)testThread, NULL);
    printf("This is the main thread!\n");
    sleep(1);
    return 0;
}

想将主函数的参数传递到testThread函数中,

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

int *testThread(void *arg) {
    printf("This is the thread test!\n");
    // arg先强转为int*型,在前面加*解引用获取整数值
    printf("input arg = %d\n", *(int*)arg);
    return NULL;
}

int main() {
    pthread_t tid;
    int rel;
    int arg = 5; // 想传递arg整形
    // &arg变成int*arg类型,再强转变为void*arg
    rel = pthread_create(&tid, NULL, (void*)testThread, (void*)&arg);
    printf("This is the main thread!\n");
    sleep(1);
    return 0;
}

线程的回收

对于一个默认属性的线程来说,线程占用的资源并不会因为执行结束而得到释放,所以需要对线程进行回收

int pthread_join(pthread_t thread, void **retval);
  • 成功时返回0,失败时返回错误码
  • thread要回收的线程对象
  • 调用线程阻塞直到thread结束
  • *retval接收线程thread的返回值

pthread_join是阻塞函数,如果回收的线程没有结束,则会一直等待

例如代码:

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

void *func(void *arg) {
    printf("This is child thread!\n");
    sleep(1);
    pthread_exit("Thread return!");
}

int main() {
    pthread_t tid;
    void *retv;
    // 先创建,再回收
    pthread_create(&tid, NULL, func, NULL);

    pthread_join(tid, &retv);
    printf("Thread ret = %s\n",(char*)retv);

    sleep(1);
}

则会先执行线程,等待线程结束后才会执行pthread_join

线程分离

方法一:

int pthread_detach(pthread_t thread);

成功时返回0,失败时返回错误号

指定该状态,线程主动与主控线程断开关系,线程结束后,不会产生僵尸进程

示例代码:

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

void *func(void *arg) {
    pthread_detach(pthread_self()); // 另一种方式
    printf("This is child thread!\n");
    sleep(1);
    pthread_exit("Thread return!");
}

int main() {
    pthread_t tid[5];
    void *retv;
    // 先创建,再回收
    for(int i = 0; i < 5; i++) {
        pthread_create(&tid[i], NULL, func, NULL);
    //  pthread_detach(tid); // detach实现线程回收
    }
    while(1) {
        sleep(1);
    }
}

方法二:

创建线程时设置为分离属性

pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);

线程的取消

int pthread_cancel(pthread_t threaed); 

注意:线程的取消要有取消点,不是所有线程都能取消,线程的取消点主要是阻塞的系统调用

例如:

void *func(void *arg) {
	printf("This is child thread\n");
	while(1) {
		sleep(1); // 取消点
	}
	pthread_exit("thread return");
}

其中的while循环就是取消点

如果没有取消点,可以自己定义一个取消点

void pthread_testcancel(void);

例如将上代码改为:

void *func(void *arg) {
	printf("This is child thread\n");
	while(1) {
		pthread_testcancel(); // 取消点
	}
	pthread_exit("thread return");
}

如果遇到取消点但是不想取消,可以设置取消点的状态

pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL); // 不能被取消
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,NULL); // 可以被取消

例如将上个代码改为:

void *func(void *arg) {
	printf("This is child thread\n");
	pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL); // 不能被取消
	while(1) {
		pthread_testcancel(); // 取消点
	}
	pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,NULL); // 可以被取消
	while(1) {
		sleep(1); // 取消点
	}
	pthread_exit("thread return");
}

第一个取消点不能被取消,第二个取消点可以被取消

线程的清理

在线程异常退出后,可以对线程的一些资源进行释放操作,打扫一下战场

void pthread_cleanup_push(void(*routine)(void*), void*arg);
void pthread_cleanup_pop(int execute);

执行条件:

被pthread_cancel取消
执行pthread_exit
非0参数执行pthread_cleanup_pop()

互斥锁

互斥锁的引入目的:当多个进程或线程访问同一个共享资源时,能确保只有一个进程或线程能访问该资源

动态方式创建:

int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexaddr_t *restrict attr);

静态方式创建:

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

锁的销毁

int pthread_mutex_destroy(pthread_mutex_t *mutex);

使用互斥锁步骤:

申请锁

#include <pthread.h>
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex)

成功时返回0,失败时返回错误码
mutex指向要初始化的互斥锁对象
pthread_mutex_lock如果无法获得锁,会阻塞
pthread_mutex_trylock如果无法获得锁,返回EBUSY,不会阻塞

读写锁

同一时刻只有一个线程可以获得写锁,但是同一时刻有多个线程可以获得读锁

当读写锁处于写锁状态,所有试图对读写锁读或者写的线程,都会被阻塞
当读写锁处于读锁状态,有写者试图加写锁时,之后的其他线程的读锁请求会被阻塞,避免长时间的不写锁

死锁的避免

  1. 锁越少越好,最好只使用一把锁
  2. 调整好锁的顺序
  • 7
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值