Linux线程的基本使用

目录

一.基本概念

二.线程

1. 线程创建

2. 线程退出

3. 线程等待

4. 线程创建Demo

三. 互斥锁

1. 创建及销毁互斥锁

2. 加锁及解锁

3. 互斥锁Demo

4.面试问题(进入死锁)

四. 条件

1. 创建及销毁条件变量

2. 等待

3. 触发

4. 条件Demo

五. 程序员检查代码小技巧


一.基本概念

典型的UNIX/Linux进程可以看成只有一个控制线程:一个进程在同一时刻只做一件事情。有了多个控制线程后,在程序设计时可以把进程设计成在同一时刻做不止一件事,每个线程各自处理独立的任务。

父进程创建子进程,都会消耗内存(拷贝)。进程是程序执行时的一个实例,是担当分配系统资源(CPU时间、内存等)的基本单位。进程本身不是基本运行单位,而是线程的容器。程序本身只是指令、数据及其组织形式的描述,进程才是程序(那些指令和数据)的真正运行实例。程序是静态的,进程是动态的。

"进程——资源分配的最小单位,线程——程序执行的最小单位"多进程程序要比多线程程序健壮。

多线程开发在 Linux 平台上已经有成熟的 pthread 库支持。其涉及的多线程开发的最基本概念主要包含三点:线程,互斥锁,条件。其中,线程操作又分线程的创建,退出,等待 3 种。互斥锁则包括 4 种操作,分别是创建,销毁,加锁和解锁。条件操作有 5 种操作:创建,销毁,触发,广播和等待。

二.线程

1. 线程创建

#include <pthread.h>
int pthread_create(pthread_t *restrict tidp, const pthread_attr_t *restrict attr, void *(*start_rtn)(void *), void *restrict arg);

pthread_create成功返回时,由tidp指向的内存单元被设置为新创建线程的线程ID。attr参数用于定制各种不同的线程属性,暂可以把它设置为NULL,以创建默认属性的线程。

新创建的线程从start_rtn函数的地址开始运行,该函数只有一个无类型指针参数arg。如果需要向start_rtn函数传递的参数不止一个,那么需要把这些参数放到一个结构中,然后把这个结构的地址作为arg参数传入。

所有代码都是   返回:若成功返回0,否则返回错误编号

2. 线程退出

int pthread_exit(void *rval_ptr);

单个线程可以通过以下三种方式退出,在不终止整个进程的情况下停止它的控制流:

  1)线程只是从启动例程中返回,返回值是线程的退出码。

  2)线程可以被同一进程中的其他线程取消。

  3)线程调用pthread_exit:

ptr是一个指针。进程中的其他线程可以调用join访问。

3. 线程等待

int pthread_join(pthread_t thread, void **rval_ptr);

调用这个函数的线程将一直阻塞,直到指定的线程调用pthread_exit、从启动例程中返回或者被取消。如果例程只是从它的启动例程返回i,如果对线程的返回值不感兴趣,可以把rval_ptr置为NULL。

4. 线程创建Demo

void *func1(void *arg)
{
        static int ret=99;                        //表示固定数据

        printf("t1 arg=%d\n",*(int *)arg);        //arg是func1的传参先(int *)转换,再取内容
        printf("t1 id=%d\n",(int)pthread_self());
        pthread_exit((void*)&ret);
}


int main ()
{
        pthread_t t1;

        int *pret=NULL;
        int arg=100;
        int ret;
        ret=pthread_create(&t1,NULL,func1,(void*)&arg);  //都是指针,先取地址,在(void*)转换
        if(ret==0){
                printf("creat yes\n");
        }
        printf("main id=%d\n",(int)pthread_self());  // 打印自己的id号

//      int pthread_join(pthread_t thread, void **rval_ptr);  
        pthread_join(t1,(void**)&pret); //先将pret取地址为二级指针,在转换为void型
        printf("pret=%d\n",*pret);
        return 0;
}

三. 互斥锁

互斥量(mutex)从本质上来说是一把锁,在访问共享资源前对互斥量进行加锁,在访问完成后释放互斥量上的锁。

定义全局变量

pthread_mutex_t mutex;

 1. 创建及销毁互斥锁

#include <pthread.h>
int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
int pthread_mutex_destroy(pthread_mutex_t mutex);
// 返回:若成功返回0,否则返回错误编号

要用默认的属性初始化互斥量,只需把attr设置为NULL。

2. 加锁及解锁

int pthread_mutex_lock(pthread_mutex_t mutex);
int pthread_mutex_trylock(pthread_mutex_t mutex);
int pthread_mutex_unlock(pthread_mutex_t mutex);
// 返回:若成功返回0,否则返回错误编号

3. 互斥锁Demo

int g_data=0;           // 全局变量
pthread_mutex_t mutex;

void *func1(void *arg)
{
        printf("t1 arg=%d\n",*(int *)arg);
        printf("t1 id=%d\n",(int)pthread_self());
        pthread_mutex_lock(&mutex);            // 上锁
        while(1){
                printf("t1=%d\n",g_data++);
                sleep(1);
        if(g_data==3){
                printf("t1= 3 let us quit\n");
                pthread_exit(NULL);
                }
        pthread_mutex_unlock(&mutex);          //解锁
        }
}

void *func2(void *arg)
{
        printf("t2 arg=%d\n",*(int *)arg);
        printf("t2 id=%d\n",(int)pthread_self());
        while(1){
                pthread_mutex_lock(&mutex);
                printf("t2=%d\n",g_data++);
                pthread_mutex_unlock(&mutex);
                sleep(1);
        }

}
int main ()
{
        int arg=100;
        int ret;
        pthread_t *t1;
        pthread_t *t2;
        t1=(pthread_t*)malloc(sizeof(pthread_t)*128);
        t2=(pthread_t*)malloc(sizeof(pthread_t)*128);    //这里如果用指针定义的话一定要开辟 
        空间否则会出现段错误

        pthread_mutex_init(&mutex,NULL);

        ret=pthread_create(t1,NULL,func1,(void*)&arg);
        
        ret=pthread_create(t2,NULL,func2,(void*)&arg);
        
        printf("main id=%d\n",(int)pthread_self());

        pthread_join(*t1,NULL);
        pthread_join(*t2,NULL);

        pthread_mutex_destroy(&mutex);
        return 0;
}

其大致作用就是解释,被夹在pthread_mutex_lock(&mutex);和 pthread_mutex_unlock(&mutex);之间的代码不受干扰,先执行完,再去执行别的线程。这个demo的作用是想,当t1线程,g_data的值为3的时候直接调用pthread_exit(NULL);退出线程,或者是exit(0);直接退出进程.这个demo不够完善,下面介绍条件时候会有最终完善效果。

4.面试问题(进入死锁)

pthread_mutex_t mutex;
pthread_mutex_t mutex2;
int i;

void *func1(void *arg)
{
        pthread_mutex_lock(&mutex);
        sleep(1);
        pthread_mutex_lock(&mutex2);
                printf("t1 arg=%d\n",*(int *)arg);
                printf("t1 id=%d\n",(int)pthread_self());
        pthread_mutex_unlock(&mutex);
}

void *func2(void *arg)
{
        pthread_mutex_lock(&mutex2);
        sleep(1);
        pthread_mutex_lock(&mutex);
        printf("t2 arg=%d\n",*(int *)arg);
        printf("t2 id=%d\n",(int)pthread_self());
        pthread_mutex_unlock(&mutex);
}

出现在有两把锁的情况下,mutex和mutex2在两个函数。列子:锁2被线程2拿走了,线程2调用锁1的时候,线程1里面有一个锁2。一直无法被调用。

四. 条件

全局变量

pthread_cond_t cond;

1. 创建及销毁条件变量

#include <pthread.h>
int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);
int pthread_cond_destroy(pthread_cond_t cond);
// 返回:若成功返回0,否则返回错误编号

除非需要创建一个非默认属性的条件变量,否则pthread_cont_init函数的attr参数可以设置为NULL。

2. 等待

int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);
int pthread_cond_timedwait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, cond struct timespec *restrict timeout);

pthread_cond_wait等待条件变为真。如果在给定的时间内条件不能满足,那么会生成一个代表一个出错码的返回变量。传递给pthread_cond_wait的互斥量对条件进行保护,调用者把锁住的互斥量传给函数。函数把调用线程放到等待条件的线程列表上,然后对互斥量解锁,这两个操作都是原子操作。这样就关闭了条件检查和线程进入休眠状态等待条件改变这两个操作之间的时间通道,这样线程就不会错过条件的任何变化。pthread_cond_wait返回时,互斥量再次被锁住。

pthread_cond_timedwait函数的工作方式与pthread_cond_wait函数类似,只是多了一个timeout。timeout指定了等待的时间,它是通过timespec结构指定。

3. 触发

int pthread_cond_signal(pthread_cond_t cond);
int pthread_cond_broadcast(pthread_cond_t cond);

这两个函数可以用于通知线程条件已经满足。pthread_cond_signal函数将唤醒等待该条件的某个线程,而pthread_cond_broadcast函数将唤醒等待该条件的所有进程。

4. 条件Demo

int g_data=0;

pthread_mutex_t mutex;

pthread_cond_t cond;

void *func1(void *arg)
{
        pthread_mutex_lock(&mutex);
        static int cnt = 0;                     //用来检测计数
        while(1){
                pthread_cond_wait(&cond,&mutex); //等待锁和条件
                sleep(1);
                printf("cond t1=%d\n",g_data);
                //      pthread_exit(NULL);
                g_data=0;                          //重置g_data为0
                sleep(1);
                if(cnt++ == 10){
                exit(1);

                }
                pthread_mutex_unlock(&mutex);       //解锁,只有完成了上述代码才能再去func2
        }
}

void *func2(void *arg)
{
        while(1){
                pthread_mutex_lock(&mutex);
                printf("t2=%d\n",g_data++);
                pthread_mutex_unlock(&mutex);

                if(g_data == 3){
                pthread_cond_signal(&cond);   //触发信号
                }
                sleep(1);
        }

}
int main ()
{
        int arg=100;
        int ret;
        pthread_t t1;
        pthread_t t2;
       
        pthread_mutex_init(&mutex,NULL);
        pthread_cond_init(&cond,NULL);   //创建条件

        ret=pthread_create(&t1,NULL,func1,(void*)&arg);
        
        ret=pthread_create(&t2,NULL,func2,(void*)&arg);
        
        pthread_join(t1,NULL);
        pthread_join(t2,NULL);

        pthread_cond_destroy(&cond);
        pthread_mutex_destroy(&mutex);  //销毁条件
        return 0;
}

运行结果:

当t2线程到3的时候触发t1线程,刷新全局变量data,再次跳转到t2线程 

五. 程序员检查代码小技巧

static int cnt = 0;
if(cnt++ == 10){
                exit(1);
                }
#include <stdio.h>
int main(int argc,char **argv)
{
        int time=atoi(argv[1]);
        int i;
        for(i=0;i<time;i++){
                system("./cond");

        }
}

10为参数,将这个代码运行100遍,放到test.txt 文件之中

撒花~ 

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在嵌入式Linux平台下,多线程之间的同步和互斥可以使用信号量来实现。信号量是一种多线程间通信的机制,用于控制多个线程对共享资源的访问。 使用信号量需要引入头文件#include <semaphore.h>。 信号量的基本操作有三个函数: 1. sem_init():初始化信号量,设置信号量的初值。 2. sem_wait():等待信号量,如果信号量值为0,则线程阻塞等待,否则信号量值减1,线程继续执行。 3. sem_post():释放信号量,将信号量值加1。 下面是一个使用信号量实现线程同步的例子: ```c #include <pthread.h> #include <semaphore.h> #include <stdio.h> sem_t sem; //定义信号量 int count = 0; //共享资源 void *thread_func(void *arg) { sem_wait(&sem); //等待信号量 count++; //对共享资源进行操作 printf("Thread %d: count = %d\n", *(int *)arg, count); sem_post(&sem); //释放信号量 pthread_exit(NULL); } int main() { pthread_t thread[5]; int i, id[5]; sem_init(&sem, 0, 1); //初始化信号量 for (i = 0; i < 5; i++) { id[i] = i; pthread_create(&thread[i], NULL, thread_func, &id[i]); } for (i = 0; i < 5; i++) { pthread_join(thread[i], NULL); } sem_destroy(&sem); //销毁信号量 return 0; } ``` 上面的例子中,共有5个线程同时对count变量进行操作,但是通过信号量的使用,保证了每次只有一个线程能够访问共享资源。信号量sem的初值为1,表示有一个线程能够访问共享资源,其他线程需要等待。每个线程在访问共享资源之前都需要等待信号量sem,如果sem为0,则线程阻塞等待,否则sem减1,线程访问共享资源。当一个线程访问完共享资源之后,需要释放信号量sem,将sem加1,其他线程就可以访问共享资源了。最后,销毁信号量sem。 需要注意的是,在使用信号量时,要保证信号量的使用线程安全的,否则会导致竞争条件的发生。在多线程环境下,使用信号量还需要考虑死锁的问题,避免出现死锁现象。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值