Linux的线程控制

进程两个特点:资源和调度,而线程是进程内的独立执行代码的实体和调度单元

进程内的线程共享进程的资源

线程间共享线程私有
进程指令
全局变量(非常容易实现线程的数据共享)
打开的文件
信号处理程序
当前工作目录
用户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;
}

执行结果如下:

### Linux 环境下线程控制教程 #### 创建线程Linux环境中,创建新线程通常使用`pthread_create()`函数。此函数接受四个参数:指向线程标识符的指针、属性设置、线程执行的起始例程以及传递给该例程的参数。 ```c #include <pthread.h> int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg); ``` 这段代码展示了如何定义和调用`pthread_create()`来启动一个新的线程[^1]。 #### 获取线程ID 每一个线程都拥有独一无二的线程ID,类型为`pthread_t`。为了获取当前正在运行线程的ID,可利用`pthread_self()`函数: ```c pthread_t tid; tid = pthread_self(); printf("Thread ID is %lu\n", (unsigned long int)tid); ``` 上述片段说明了怎样取得并打印出当前线程的身份编号。 #### 终止线程 有几种方法可以让一个线程结束自己的生命周期。一种方式是让其返回自动生成的结果值;另一种则是显式地调用`pthread_exit()`函数,并传入状态码作为退出原因。 ```c void pthread_exit(void *retval); // 或者在线程函数内直接return某个值; void* thread_function(void *arg){ ... return NULL; // 表示正常终止 } ``` 这里解释了两种不同的途径使得线程能够安全地中止运作[^3]。 #### 同步机制 为了避免竞争条件的发生,需要采用同步手段如互斥锁(`mutex`)来进行保护共享资源访问。下面是一个简单的例子展示如何声明、初始化及销毁一把互斥锁。 ```c pthread_mutex_t mutex; // 初始化互斥锁 if (pthread_mutex_init(&mutex, NULL) != 0) { printf("\n mutex init failed\n"); return 1; } // 锁住临界区之前... pthread_mutex_lock(&mutex); // ...解锁之后 pthread_mutex_unlock(&mutex); // 当不再需要时释放资源 pthread_mutex_destroy(&mutex); ``` 这部分内容介绍了基本的互斥锁定操作以确保多线程间的协调一致[^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值