线程

1、概念

进程并不是CPU使用的基本单元,线程才是CPU使用的一个基本单元,一个进程可以有多个线程,同一个进程中的线程共享代码段、数据段和其他系统资源。但是一个进程中的线程是有各自的寄存器,堆栈的。 首先明确两个概念:并行性和并发性 并行性:多个核,同时执行多个进程或线程。 并发性:以前的CPU单核的,一次只能执行一个,但是通过调度,可以执行多个进程的,这种情况就是并发性。

线程技术的优势 (1)像进程一样可被OS调度 (2)同一进程的多个线程之间很容易高效率通信 (3)在多核心CPU(对称多处理器架构SMP)架构下效率最大化

2、多线程模型

线程有两种,用户线程核内核线程。 用户线程位于内核之上,不需要内核支持,只是有本地的线程库即可。优点是效率高,不是系统调用,节约开支。缺点是内核并不认识你是不是线程,多个线程也并不能运行在多核处理器上,而且如果一个线程阻塞住,所有的线程都被阻塞,进程将被阻塞。 内核线程是内核自己实现的,没有上面的用户线程的缺点,是真正的线程,但是线程需要系统调用,多了一些开支,所以效率低一些,但是更安全,现在基本使用的都是内核线程。

在LinuxAPI中我已经详细写了创建线程,销毁线程的一系列代码,这里不在赘述。

3、线程池

现在的系统中一班都实现了线程池。线程池就是在进程开始时创建一定数量的线程,并加到池中等待工作,当服务器收到请求,会唤醒一个线程,并将需要的服务传递给他,一旦线程完成服务,在返回到池中等待。

4、进程的同步、互斥、通信的区别,进程与线程同步的区别

https://blog.csdn.net/weixin_41413441/article/details/80548683

5、线程操作

线程常见函数 1、线程创建与回收

函数名

作用

pthread_create

主线程用来创造子线程的

pthread_join

主线程用来等待(阻塞)回收子线程

pthread_detach

主线程用来分离子线程,分离后主线程不必再去回收子线程

2、线程取消

函数名

作用

pthread_cancel

一般都是主线程调用该函数去取消(让它赶紧死)子线程

pthread_setcancelstate

子线程设置自己是否允许被取消

pthread_setcanceltype

3、线程函数退出相关

函数名

作用

pthread_exit

退出

return

退出

pthread_cleanup_push

这两个函数貌似和堆栈有关

pthread_cleanup_pop

4、获取线程id

pthread_self

线程同步之信号量 1、任务:用户从终端输入任意字符然后统计个数显示,输入end则结束 使用多线程实现:主线程获取用户输入并判断是否退出,子线程计数 其实可以不用多线程来实现这个任务,但是我们只是用这个例子来演示多线程的使用方法

6、信号量的介绍和使用

信号量和互斥锁都是系统层面实现的,其本质是因为硬件实现了原子操作,不可打断,操作系统进行了封装,方面我们使用,才有了信号量和互斥锁

在我们使用上面的函数创建进程之后,进程之间不能够实现同步的功能,所以这里我们引入信号量。使用的是Linux中的库函数。信号量相关函数如下:

信号量初始化函数
#include <semaphore.h>
int sem_init(sem_t *sem, int pshared, unsigned int value);
参数中的pshared和value一般为0,sem需要自己定义。
激活信号量
#include <semaphore.h>
int sem_post(sem_t *sem);
用于阻塞,成功就会返回0
#include <semaphore.h>
int sem_wait(sem_t *sem);
销毁信号量
#include <semaphore.h>
int sem_destroy(sem_t *sem);

需要声明的是,在终端中编译Linux线程相关的代码时,要在gcc后面加上-lpthread ,这是因为:

pthread 库不是 Linux 系统默认的库,连接时需要使用静态库 libpthread.a,所以在使用pthread_create()创建线程,以及调用 pthread_atfork()函数建立fork处理程序时,需要链接该库。 在编译中要加 -lpthread参数     gcc thread.c -o thread -lpthread     thread.c为你些的源文件,不要忘了加上头文件#include<pthread.h>

#include <stdio.h> 
#include <string.h> 
#include <stdlib.h> 
#include <pthread.h> 
#include <semaphore.h> 

char buf[200] = {0};
sem_t sem;//需要作为全局变量,子线程也能用
unsigned int flag = 0;


// 子线程程序,作用是统计buf中的字符个数并打印 
void *func(void *arg)
{        
    // 阻塞在等待主线程激活的时候,子线程被激活后就去获取buf中的字符     
    // 长度,然后打印;完成后再次被阻塞     
    sem_wait(&sem);    
    while (flag == 0)
    {    
        printf("本次输入了%d个字符\n", strlen(buf));
        memset(buf, 0, sizeof(buf));
        sem_wait(&sem);
    }
    pthread_exit(NULL);
}


int main(void)
{
    int ret = -1;
    pthread_t th = -1;
    
    sem_init(&sem, 0, 0);
    ret = pthread_create(&th, NULL, func, NULL);
    if (ret != 0)
    {
        printf("pthread_create error.\n");
        exit(-1);
    }
    
    printf("输入一个字符串,以回车结束\n");
    while (scanf("%s", buf))
    {
        // 去比较用户输入的是不是end,如果是则退出,如果不是则继续            
        if (!strncmp(buf, "end", 3))
        {
            printf("程序结束\n");
            flag = 1;
            sem_post(&sem);            
            break;
        }
        
        // 主线程在收到用户收入的字符串,并且确认不是end后         
        // 就去发信号激活子线程来计数。         
        // 子线程被阻塞,主线程可以激活,这就是线程的同步问题。         
        // 信号量就可以用来实现这个线程同步         
        sem_post(&sem);    
    }

    
    // 回收子线程     
    printf("等待回收子线程\n");
    ret = pthread_join(th, NULL);
    if (ret != 0)
    {
        printf("pthread_join error.\n");
        exit(-1);
    }
    printf("子线程回收成功\n");
    
    sem_destroy(&sem);
    
    return 0;
}

7、互斥锁

(1)互斥锁又叫互斥量(mutex)

(2)相关函数:

pthread_mutex_init //初始化互斥锁 pthread_mutex_destroy //销毁互斥锁 pthread_mutex_lock //上锁 pthread_mutex_unlock //解锁

(3)互斥锁和信号量的关系:可以认为互斥锁是一种特殊的信号量

(4)互斥锁主要用来实现关键段保护,保护这段代码不被别人访问。互斥锁上锁的代码,就不可以访问了,从而实现保护,是阻塞的。

用互斥锁来实现的代码

#include <stdio.h> 
#include <string.h> 
#include <stdlib.h> 
#include <pthread.h> 



char buf[200] = {0};
pthread_mutex_t mutex;
unsigned int flag = 0;


// 子线程程序,作用是统计buf中的字符个数并打印 
void *func(void *arg)
{
    // 子线程首先应该有个循环     
    // 循环中阻塞在等待主线程激活的时候,子线程被激活后就去获取buf中的字符     
    // 长度,然后打印;完成后再次被阻塞     
    
    //while (strncmp(buf, "end", 3) != 0)     
    sleep(1);
    while (flag == 0)
    {    
        pthread_mutex_lock(&mutex);
        printf("本次输入了%d个字符\n", strlen(buf));
        memset(buf, 0, sizeof(buf));
        pthread_mutex_unlock(&mutex);
        sleep(1);
    }
    
    
    pthread_exit(NULL);
}


int main(void)
{
    int ret = -1;
    pthread_t th = -1;
    
    
    
    pthread_mutex_init(&mutex, NULL);
    
    ret = pthread_create(&th, NULL, func, NULL);
    if (ret != 0)
    {
        printf("pthread_create error.\n");
        exit(-1);
    }
    
    printf("输入一个字符串,以回车结束\n");
    while (1)
    {
        pthread_mutex_lock(&mutex);
        scanf("%s", buf);
        pthread_mutex_unlock(&mutex);
        // 去比较用户输入的是不是end,如果是则退出,如果不是则继续            
        if (!strncmp(buf, "end", 3))
        {
            printf("程序结束\n");
            flag = 1;
            
            //exit(0);             
                        break;
        }
        sleep(1);
        // 主线程在收到用户收入的字符串,并且确认不是end后         
        // 就去发信号激活子线程来计数。         
        // 子线程被阻塞,主线程可以激活,这就是线程的同步问题。         
        // 信号量就可以用来实现这个线程同步         
    }

    
    // 回收子线程     
    printf("等待回收子线程\n");
    ret = pthread_join(th, NULL);
    if (ret != 0)
    {
        printf("pthread_join error.\n");
        exit(-1);
    }
    printf("子线程回收成功\n");
    
    pthread_mutex_destroy(&mutex);
    
    return 0;
}

8、线程同步之条件变量

什么是条件变量

相关函数

​ pthread_cond_init pthread_cond_destroy

​ pthread_cond_wait pthread_cond_signal/pthread_cond_broadcast

使用条件变量来实现代码

条件变量和互斥锁之间有一定的关联。

#include <stdio.h> 
#include <string.h> 
#include <stdlib.h> 
#include <pthread.h> 


char buf[200] = {0};
pthread_mutex_t mutex;
pthread_cond_t cond;
unsigned int flag = 0;


// 子线程程序,作用是统计buf中的字符个数并打印 
void *func(void *arg)
{
    // 子线程首先应该有个循环     
    // 循环中阻塞在等待主线程激活的时候,子线程被激活后就去获取buf中的字符     
    // 长度,然后打印;完成后再次被阻塞     
    //while (strncmp(buf, "end", 3) != 0)     
    //sleep(1);     
    while (flag == 0)
    {    
        pthread_mutex_lock(&mutex);
        pthread_cond_wait(&cond, &mutex);
        printf("本次输入了%d个字符\n", strlen(buf));
        memset(buf, 0, sizeof(buf));
        pthread_mutex_unlock(&mutex);
        //sleep(1);     }
    
    
    pthread_exit(NULL);
}


int main(void)
{
    int ret = -1;
    pthread_t th = -1;
    

    pthread_mutex_init(&mutex, NULL);
    pthread_cond_init(&cond, NULL);
    
    ret = pthread_create(&th, NULL, func, NULL);
    if (ret != 0)
    {
        printf("pthread_create error.\n");
        exit(-1);
    }
    
    printf("输入一个字符串,以回车结束\n");
    while (1)
    {
        //pthread_mutex_lock(&mutex);         
        scanf("%s", buf);
        pthread_cond_signal(&cond);
        //pthread_mutex_unlock(&mutex);         
        // 去比较用户输入的是不是end,如果是则退出,如果不是则继续            
        if (!strncmp(buf, "end", 3))
        {
            printf("程序结束\n");
            flag = 1;
            
            //exit(0);             break;
        }
        
        //sleep(1);         
        // 主线程在收到用户收入的字符串,并且确认不是end后         
        // 就去发信号激活子线程来计数。         
        // 子线程被阻塞,主线程可以激活,这就是线程的同步问题。         
        // 信号量就可以用来实现这个线程同步         
    }

    
    // 回收子线程     
    printf("等待回收子线程\n");
    ret = pthread_join(th, NULL);
    if (ret != 0)
    {
        printf("pthread_join error.\n");
        exit(-1);
    }
    printf("子线程回收成功\n");
    
    pthread_mutex_destroy(&mutex);
    pthread_cond_destroy(&cond);
    
    return 0;
}

版权属于:孟超 本文链接:https://mengchao.xyz/index.php/archives/272/ 转载时须注明出处及本声明

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 挂载根文件系统

    Linux中就有制作根文件系统的工具,mke2fs.只需要找到一个目录,然后依次执行下面的命令就行

    用户5426759
  • 汇编一个简单的 C 语言程序并分析其汇编指令执行过程

    如果想把 main.c 编译成一个汇编代码,那么可以使用如下命令: gcc –S –o main.s main.c –m32 上述命令产生一个以“.s”作为扩展...

    用户5426759
  • 汇编、Linux API面试题

    ③存储器寻址:直接寻址,寄存器间接寻址,基址变址寻址,基址变址寻址且相对寻址,寄存器相对寻址

    用户5426759
  • 漫画:并发系列 之 H2O的生成

    水分子的产生:现在有两种线程,氢 oxygen 和氧 hydrogen,你的目标是组织这两种线程来产生水分子。

    程序员小浩
  • 商城项目-跨域问题

    而我们刚才是从manage.leyou.com去访问api.leyou.com,这属于二级域名不同,跨域了。

    cwl_java
  • 异步事务?关于异步@Async + 事务@Transactional的结合使用问题分析【享学Spring MVC】

    如题,如果把这两者拆开来看的话,两者你应该都不陌生:@Async你不陌生,@Transactional你应该更不陌生,若单独拿来使用,理论上应该木有不会用的吧。...

    YourBatman
  • spring: 加载远程配置

    通常在spring应用中,配置中的properties文件,都是打包在war包里的,部署规模较小,只有几台服务器时,这样并没有什么大问题。如果服务器多了,特别是...

    菩提树下的杨过
  • flowable实现流程全局事件

    最近在研究flowable,发现这个东东虽说是activiti的升级版,但感觉还是没有a5的好用。 项目中需要实现一个全局事件,实现如下:

    星痕
  • 视频会议玩消失?借助神器TensorFlow.js,200行代码教你“隐身”,GitHub标星3k+

    疫情期间,许多公司都开启了远程办公,试想一下,在视频会议时,如果你的同事突然从镜头中消失,大家会有什么样的反应?

    大数据文摘
  • 1036 跟奥巴马一起编程 (15 分)

    美国总统奥巴马不仅呼吁所有人都学习编程,甚至以身作则编写代码,成为美国历史上首位编写计算机代码的总统。2014 年底,为庆祝“计算机科学教育周”正式启动,奥巴马...

    可爱见见

扫码关注云+社区

领取腾讯云代金券