Linux应用开发之线程


前言

多线程编程已经是大多数应用软件的必备技能,下面将学习一些线程编程的基础知识


提示:以下是本篇文章正文内容

一、线程是什么?

线程是参与系统调度的最小单位。 它被包含在进程之中, 是进程中的实际运行单位。一个线程指的是进
程中一个单一顺序的控制流(或者说是执行路线、执行流), 一个进程中可以创建多个线程, 多个线程实现并发运行, 每个线程执行不同的任务。 譬如某应用程序设计了两个需要并发运行的任务 task1 和 task2,可将两个不同的任务分别放置在两个线程中。

二、线程与进程的区别

多进程编程的劣势:

  • 进程间切换开销大。多个进程同时运行(指宏观上同时运行,无特别说明,均指宏观上),微观上
    依然是轮流切换运行,进程间切换开销远大于同一进程的多个线程间切换的开销,通常对于一些中
    小型应用程序来说不划算。
  • 进程间通信较为麻烦。 每个进程都在各自的地址空间中、相互独立、隔离,处在于不同的地址空间
    中,因此相互通信较为麻烦

多线程编程的优势:

  • 同一进程的多个线程间切换开销比较小。
  • 同一进程的多个线程间通信容易。 它们共享了进程的地址空间,所以它们都是在同一个地址空间
    中,通信容易。
  • 线程创建的速度远大于进程创建的速度。

三、线程怎么使用

1.线程ID

如每个进程都有一个进程ID一样,每个线程也有其对应的线程ID,线程ID使用pthread_t数据类型表示

#include<pthread.h>
//获取自己的线程ID
pthread_t pthread_self(void);
//检查两个线程是否相等
int pthread_equal(pthread_t t1, pthread_t t2);
	return:如果两个线程ID相等,则返回一个非0值,否则返回0.

2.创建线程

代码如下(示例):

#inlcude<pthread.h>

int pthread_create(pthread_t thread, const pthread_attr_t *attr, void*(*start_routine)(void*), void *arg);
	args:
		thread:pthread_t类型指针,线程的唯一标识,线程ID会保存在此指针指向的内存中。
		attr:pthread_attr_t类型指针,设置了线程的各种属性,如果设置为NULL,则所有属性设置为默认值
		start_routine:是一个函数指针,新创建的线程从此函数运行,如果此函数的参数需要一个以上,需要把参数放到结构体中,从arg传入
		arg:传递给start_routine的参数,设置为NULL表示不需要参数
		return:成功返回0,失败返回一个错误号。

3.终止线程

#include <pthread.h>

void pthread_exit(void *retval);
	

4.回收线程

在线程当中,通过调用pthread_join()来阻塞等待线程的终止,并获取线程的退出码,回收线程资源

#include <pthread.h>

int pthread_join(pthread_t thread, void **retval);
	args:
		thread:指定需要等待的线程ID
		retval:将线程的退出状态复制到*retval指向的内存中,如果目标被pthread_cancel()取消,则将PTHREAD_CANCELED放在*retval中,如果不获取退出状态,则设置为NULL
		return:成功返回0,失败返回错误码

5.取消线程

向一个线程发送请求,要求它立刻退出

#include <pthread.h>

int pthread_cancel(pthread_t thread);
	return:成功返回0,失败返回错误码

6.取消状态以及类型

线程接收到其他线程发送的请求,可以选择不被取消,或者控制如何被取消

#include <pthread.h>

int pthread_setcancelstate(int state, int *oldstate);
	args:
		state:	PTHREAD_CANCEL_ENABLE:线程可以取消,这是新创建线程取消状态的默认值
				PTHREAD_CANCEL_DISABLE:线程不可被取消,如果线程接收到取消请求,则会将线程挂起,直到线程的取消状态变为PTHREAD_CANCEL_ENABLE
		return:成功返回0,失败返回非0的错误码
int pthread_setcanceltype(int type, int *oldtype);
	args:
		type:	PTHREAD_CANCEL_DEFERRED:取消请求到来时,线程还是会继续运行,取消请求被挂起,直到线程到达某个取消点
				PTHREAD_CANCEL_ASYNCHRONOUS:可能会在任何时间点取消线程
		return:成功返回0,失败返回非0的错误码

7.线程可取消性检测

#include<pthread.h>
//产生一个取消点
void pthread_testcancel(void);

8.分离线程

有时候我们不想关心线程的返回状态,希望它终止时可以自动回收线程资源并将其移除。

#include <pthread.h>

int pthread_detach(pthread_t thread);

9.线程清理函数

每个线程都有一个清理函数栈,栈是一种先进后出的数据结构,也就是说它们的执行顺序与注册顺序相反,执行完清理函数时,线程终止。

#inlcude <pthread.h>

#向清理函数栈中添加一个清理函数
void pthread_cleanup_push(void(*routine)(void*), void *arg);
#将清理函数栈中最顶层的函数移除
void pthread_cleanup_pop(int excute);

10.线程属性

调用pthread_create()创建线程,可以对新线程设置各种属性。可以使用pthread_attr_t数据类型定义线程的所有属性。

#include <pthread.h>

#初始化pthread_attr_t对象
int pthread_attr_init(pthread_attr_t *attr);
#销毁pthread_attr_t对象
int pthread_attr_destroy(pthread_attr_t *attr);

#对栈起始地址和栈大小进行设置
int pthread_attr_setstack(pthread_attr_t *attr, void *stackaddr, size_t stacksize);
#获取栈起始地址和栈大小
int pthread_attr_getstack(const pthread_attr_t *attr, coid **stackaddr, size_t *stacksize);
#单独获取或设置栈大小、栈起始地址
int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize);
int pthread_attr_getstacksize(const pthread_attr_t *attr, size_t *stacksize);
int pthread_attr_setstackaddr(pthread_attr_t *attr, void *stackaddr);
int pthread_attr_getstackaddr(const pthread_attr_t *attr, void **stackaddr);

#设置detachstate线程属性
int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
#获取detachstate线程属性
int pthread_attr_getdetachstate(const pthread_attr_t *attr, int detachstate);
	args:
		detachstate:PTHREAD_CREATE_DETACHED 分离属性
		PTHREAD_CREATE_JOINABLE 默认属性

四、线程同步

主要是为了数据一致性的问题

1.互斥锁

同一时间只有一个线程能够获得互斥锁,其他线程陷入阻塞等待

#include <pthread.h>

#初始化互斥锁
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr);
#加锁
int pthread_mutex_lock(pthread_mutex_t *mutex);
#解锁
int pthread_mutex_unlock(pthread_mutex_t *mutex);
#加锁线程不被阻塞
int pthread_mutex_trylock(pthread_mutex_t *mutex);
#销毁锁
int pthread_mutex_destroy(pthread_mutex_t *mutex);
#互斥锁属性对象的创建和销毁
int pthread_mutexattr_init(pthread_mutexattr_t *attr);
int pthread_mutexattr_destroy(pthread_mutexattr_t *attr);
#互斥锁类型属性的查询与修改
int pthread_mutexattr_gettype(const pthread_mutexattr_t *attr, int type);
int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type);

2.条件变量

等待某个条件满足而被阻塞,搭配互斥锁使用

#include <pthread.h>

#条件变量初始化
int pthread_cond_destroy(pthread_cond_t *cond);
int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr);
#通知和等待条件变量
int pthread_cond_broadcast(pthread_cond_t *cond);
int pthread_cond_signal(pthread_cond_t *cond);
#将线程设置为等待状态
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);

3.自旋锁

适用于等待时间短的场景

#include <pthread.h>

#自旋锁初始化
int pthread_spin_destroy(pthread_spinlock_t *lock);
int pthread_spin_init(pthread_spinlock_t *lock, int pshared);
	args:
		pshared:进程共享属性
				PTHREAD_PROCESS_SHARED: 共享自旋锁。该自旋锁可以在多个进程中的线程之间共享
				PTHREAD_PROCESS_PRIVATE: 私有自旋锁。只有本进程内的线程才能够使用该自旋锁	

#加锁与解锁
int pthread_spin_lock(pthread_spinlock_t *lock);
int pthread_spin_trylock(pthread_spinlock_t *lock);
int pthread_spin_unlock(pthread_spinlock_t *lock);

4.读写锁

有三种模式:
读加锁状态
写加锁状态
不加锁状态

#include <pthread.h>

#读写锁的初始化
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
int pthread_rwlock_init(pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr);

#加锁与解锁
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);

#非阻塞加锁
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);

#读写锁属性
int pthread_rwlockattr_destroy(pthread_rwlockattr_t *attr);
int pthread_rwlockattr_init(pthread_rwlockattr_t *attr);
#获取与设置属性
int pthread_rwlockattr_getpshared(const pthread_rwlockattr_t *attr, int *pshared);
int pthread_rwlockattr_setpshared(pthread_rwlockattr_t *attr, int pshared);
args:
	pshared:PTHREAD_PROCESS_SHARED: 共享读写锁。该读写锁可以在多个进程中的线程之间共享
			 PTHREAD_PROCESS_PRIVATE: 私有读写锁。只有本进程内的线程才能够使用该读写锁		

总结

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

梦成大佬的第N天

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值