进程两个特点:资源和调度,而线程是进程内的独立执行代码的实体和调度单元
进程内的线程共享进程的资源
线程间共享 线程私有 进程指令
全局变量(非常容易实现线程的数据共享)
打开的文件
信号处理程序
当前工作目录
用户ID和组ID线程ID
PC指针(标识当前线程的执行位置)
线程栈
线程控制
获取线程ID:pthread_t pthread_self(void)
返回值:成功:返回线程ID; 失败:无!
线程ID:pthread_t类型,本质:在Linux下为无符号整数(%lu)线程的创建:int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg)
返回值:
成功:0; 失败:错误号 -----Linux环境下,所有线程特点,失败均直接返回错误号。
参数:
pthread_t:当前Linux中可理解为:typedef unsigned long int pthread_t;
参数1:传出参数,保存系统为我们分配好的线程ID
参数2:通常传NULL,表示使用线程默认属性。若想使用具体属性也可以修改该参数。
参数3:函数指针,指向线程主函数(线程体),该函数运行结束,则线程结束。
参数4:线程主函数执行期间所使用的参数,如要传多个参数, 可以用结构封装线程的终止:int pthread_join(pthread_t thread, void **retval)
返回值 : 0代表成功。 失败,返回的则是错误号。
参数 :thread: 线程标识符,即线程ID,标识唯一线程。
retval: 用户定义的指针,用来存储被等待线程的返回值。线程有三种终止方式:
1、自己返回退出
2、被同一进程中的其他线程请求取消(int pthread_cancel(pthread_t thread);)
3、线程在任意函数中调用pthread_exit()函数执行终止(void pthread_exit(void *retval);)
若A,B线程为同一进程中的两个线程,A调用pthread_cancel(B)等价于B调用pthread_exit()进程与线程的对比
线程的数据共享
临界资源:在一段时间内只允许一个任务(线程/进程)访问的资源。
静态初始化:pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER
动态初始化:int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t*restrict attr)
参数:mutex: 要初始化的互斥量的地址
attr: 互斥量的属性(一般设置为NULL)加锁:int pthread_mutex_lock(pthread_mutex_t *mutex)
线程调用加锁时,若互斥量被其他线程加锁,则该线程阻塞,直到可以完成加锁操作位置。如果不想让互斥量的加锁让线程阻塞等待,则可以调用:int pthread_mutex_trylock(pthread_mutex_t *mutex);
解锁:int pthread_mutex_unlock(pthread_mutex_t *mutex)
销毁互斥量变量:int pthread_mutex_destroy(pthread_mutex_t *mutex)
条件变量
多个线程访问临界资源有需要相互合作。如线程A先需要某种操作(修改全局变量X)后,线程B才能(根据全局变量X判断)执行另一操作。这就涉及到了线程执行中的条件变量
条件变量与互斥量一起使用时,允许线程以互斥的方式阻塞等待条件的发送(同步)触发条件线程x:
互斥量加锁 -> x操作-> 触发条件变量 -> 互斥量解锁
等待条件线程y:
互斥量加锁 -> 等待条件变量-> y操作 -> 互斥量解锁
静态初始化:pthread_cond_t cond = PTHREAD_COND_INITIALIZER
动态初始化:int pthread_cond_init(pthread_cond_t *cond,pthread_condattr_t *cond_attr)
参数:cond: 一个指向结构pthread_cond_t的指针
cond_attr: 一个指向结构pthread_condattr_t的指针等待条件:
阻塞条件变量:int pthread_cond_wait(pthread_cond_t *cond,pthread_mutex_t *mutex)
指定时间内阻塞条件:int pthread_cond_timewait(pthread_cond_t *cond,pthread_mutex *mutex,const timespec *abstime)销毁:int pthread_cond_destroy(pthread_cond_t *cond);
触发条件变量:
int pthread_cond_signal(pthread_cond_t *cond); //唤醒等待的线程中的某一线程
int pthread_cond_broadcast(pthread_cond_t *cond); //唤醒等待的线程中的所有线程,这些线程进行竞争
代码实例1如下:
/**************************************
作 者 : lijd
生成日期 : 2020年12月29日
功能描述 : 两个线程对全局变量操作,两个条件:
1、当count大于0时才能做减1操作
2、当count小于等于0时只能做加1操作
**************************************/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
pthread_mutex_t count_lock;
pthread_cond_t count_ready;
int count = 0;
// 对全局变量做减1操作
void *dec(void *arg)
{
printf("dec thread:%ld\n", pthread_self());
pthread_mutex_lock(&count_lock);
printf("des waiting\n");
if(count <= 0)
{
pthread_cond_wait(&count_ready, &count_lock); // 阻塞等待
}
count = count - 1;
printf("des count = %d\n", count);
pthread_mutex_unlock(&count_lock);
printf("des quit!\n");
pthread_exit(NULL);
}
// 对全局变量做加1操作
void *inc(void *arg)
{
printf("inc thread:%ld\n", pthread_self());
pthread_mutex_lock(&count_lock);
printf("inc running\n");
count = count + 1;
printf("inc count = %d\n", count);
if(count > 0)
{
pthread_cond_signal(&count_ready); // 唤醒等待的线程中的某一线程
}
pthread_mutex_unlock(&count_lock);
printf("inc quit!\n");
pthread_exit(NULL);
}
int main(void) {
pthread_t tid1,tid2;
printf("main thread:%ld\n", pthread_self());
pthread_mutex_init(&count_lock, NULL);
pthread_cond_init(&count_ready, NULL);
// 先创建tid1线程des函数
pthread_create(&tid1, NULL, dec, NULL);
sleep(2);
// 后创建tid2线程inc函数
pthread_create(&tid2, NULL, inc, NULL);
printf("tid1:%ld, tid2:%ld\n", tid1, tid2);
pthread_join(tid2, NULL);
pthread_join(tid1, NULL);
return 0;
}
执行结果如下:
读写锁
问题描述:
在对临界资源的访问中,更多的是读操作,写操作较少,只有互斥量机制会影响访问效率。期望对临界资源的访问控制粒度更精细,从而引出读写锁。
读写锁对共享资源的访问者划分成读者和写者,读者只对共享资源进行读访问,写者则需要对共享资源进行写操作互斥关系:
读操作 - 写操作 互斥
写操作 - 写操作 互斥
读操作 - 读操作 不互斥同步关系:
缓冲区不满,才允许写操作;缓冲区不空,才允许读操作。访问控制规则:
如果有线程对互斥资源进行读操作,则允许其他线程执行读操作,但不允许任何线程执行写操作;
如果有线程对互斥资源进行写操作,则不允许任何线程执行读操作和写操作;读写锁操作API:
静态初始化:pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER
动态初始化:int pthread_rwlock_init(pthread_rwlock_t *rwptr, const pthread_rwlockattr_t *attr)
参数:rwptr: 要初始化的读写锁的地址
attr: 读写锁的属性(一般设置为NULL)
销毁: int pthread_rwlock_destroy(pthread_rwlock_t *rwptr)阻塞加读锁:int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock)
非阻塞加读锁:int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock)阻塞加写锁:int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock)
非阻塞加写锁:int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock)释放读锁或写锁:int pthread_rwlock_unlock(pthread_rwlock_t *rwlock)
代码实例2如下:
/**************************************
作 者 : lijd
生成日期 : 2020年12月29日
功能描述 : 验证读写锁机制
**************************************/
#include <stdio.h>
#include <stdlib.h>
#include<pthread.h>
#include<string.h>
#include <sys/time.h>
#define WRNUM 2
#define RDNUM 2
#define STR_NUM 3
char test[STR_NUM] = "123";
// 定义全局读写锁
pthread_rwlock_t rwlock;
// 获取的是秒 (1秒 = 1000毫秒 =1000000微妙)
long getCurrentTime()
{
struct timeval tv;
gettimeofday(&tv,NULL);
return (tv.tv_sec * 1000 + tv.tv_usec/1000)/1000;
}
void PthWrite()
{
char w_str[] = "ABC";
int i = 0;
for(i = 0; i < STR_NUM; i++)
{
pthread_rwlock_wrlock(&rwlock);
test[i] = w_str[i];
printf("time:%lu, PthWrite ID:%lu, Write:%c, iNum:%u, Now Sleeping Start...\n",getCurrentTime(), pthread_self(), w_str[i], i);
sleep(1);
printf("time:%lu, PthWrite ID:%lu, iNum:%u, Now Sleeping End!\n",getCurrentTime(), pthread_self(), i);
pthread_rwlock_unlock(&rwlock);
sleep(1);
}
pthread_rwlock_destroy(&rwlock);
}
void PthRead()
{
int i = 0;
for(i = 0; i < STR_NUM; i++)
{
pthread_rwlock_rdlock(&rwlock);
printf("time:%lu, PthRead ID:%lu, Read:%c, iNum:%u, Now Sleeping Start...\n",getCurrentTime(), pthread_self(), test[i], i);
sleep(1);
printf("time:%lu, PthRead ID:%lu, iNum:%u, Now Sleeping End!\n",getCurrentTime(), pthread_self(), i);
pthread_rwlock_unlock(&rwlock);
sleep(1);
}
pthread_rwlock_destroy(&rwlock);
}
int main()
{
pthread_t rd[WRNUM], wr[RDNUM];
int i;
pthread_rwlock_init(&rwlock, NULL);
for(i = 0; i < WRNUM; i++)
{
pthread_create(&wr[i], NULL, PthWrite, NULL);
}
for(i = 0; i < RDNUM; i++)
{
pthread_create(&rd[i], NULL, PthRead, NULL);
}
for(i = 0; i < WRNUM; i++)
{
pthread_join(wr[i], NULL);
}
for(i = 0; i < RDNUM; i++)
{
pthread_join(rd[i], NULL);
}
return 0;
}
执行结果如下: