zoukankan      html  css  js  c++  java
  • C语言基础-线程

    线程概念

    进程:

    • 进程有独立的地址空间
    • Linux为每个进程创建task_structt
    • 每个进程都参与内核调度,互不影响

    线程:

    • 进程在切换时系统开销大
    • 很多操作系统引入轻量级进程LWP
    • 同一进程中的线程共享相同地址空间
    • linux不区分进程、线程

    线程资源

    • 共享资源
      • 可执行的命令
      • 静态数据
      • 进程中打开的文件描述符
      • 当前工作目录
      • 用户ID
      • 用户组ID
    • 私有资源
      • 线程ID(TID)
      • PC(程序计数器)和相关寄存器
      • 堆栈
      • 错误号(errno)
      • 优先级
      • 执行状态和属性

    使用线程的好处

    • 大大提高了任务切换的效率
    • 避免了额外的TLB&cache的刷新

    Linux线程库-pthread

    如果进程结束,进程下的所有线程都会被结束

    线程的创建-pthread_create

    #include <pthread.h>
    int pthread_create(pthread_t *thread,const pthread_attr_t *attr,void *(routine)(void *),void *arg);
    
    // 成功返回0
    // 失败返回错误码
    // tread线程对象
    // attr 线程属性,NULL代表默认属性
    // *(routine)(void *) routine线程执行的函数
    // arg传递给现场执行函数的参数

    线程的回收-pthread_join

    #include <pthread.h>
    int pthread_join(pthread_t thread,void **retval);
    
    // 成功返回0
    // 失败时返回错误码
    // thread 要回收的线程对象
    // *retval 接受线程thread的返回值
    // 调用线程阻塞知道thread的返回值

    线程的结束-pthread_exit

    #include <pthread.h>
    void pthread_exit(void *retval);
    
    // 结束当前线程 或指定到函数的return结束
    // retval可被其他线程通过pthread_join获取
    // retval不能是局部变量否则会出错
    // 线程私有资源被释放

    Demo:

    #include <pthread.h>
    #include <stdio.h>
    #include <zconf.h>
    #include <string.h>
    
    char message[32] = "hello World";
    
    void *thread_func(void *arg) {
        sleep(1);
        strcpy(message, "thread hello");
        // 线程退出
        // pthread_exit("thread over");
        return "thread over"; // 与上方pthread_exit效果相同
    }
    
    int main(int argc, char *argv[]) {
        pthread_t a_thread;
        void *result;
        // 创建线程
        if (pthread_create(&a_thread, NULL, thread_func, NULL) != 0) {
            perror("pthread_create");
        }
        // 线程回收
        pthread_join(a_thread, &result);
        printf("result:%s
    ", result); // result接受线程的返回值
        // 查看线程是否继承了进程的资源
        printf("message:%s
    ", message);
        return 0;
    }
    
    // Ps:编译一定要链接 否则会报错
    // $gcc -o test test.c -l pthread

     

    线程间通讯

    优点:线程间通讯很容易(通过全局变量交换数据)

    缺点:多个线程 访问共享数据时需要同步或互斥机制

    同步机制

    同步(synchronization)指的是多个任务按照约定的先后次序相互配合完成一件事

    由信号量来决定线程是继续运行还是阻塞等待

     

    信号量

    • 信号量代表某一类资源,其值表示系统中该资源的数量
    • 信号量是一个受保护的变量,只能通过三种操作来访问
      • 初始化
      • P操作(申请资源)
      • V操作(释放资源)

    信号量P/V操作

    • P(S)含义如下
    if (信号量的值大于0){申请资源的任务继续运行;信号量的值减一;}
    else{申请资源的任务阻塞;}
    • V(S)含义如下
    if(有任务在等待资源){唤醒等待任务,让其继续运行}

    Posix信号量

    • 无名信号量(基于内存的信号量)
    • 有名信号量(既可以用线程,也可以用于进程)

    信号量的初始化-sem_init

    #include <semaphore.h>
    int sem_init(sem_t *sem,int pshared,unsigned int val);
    
    // 信号量被创建后必须初始化
    // 成功时返回0
    // 失败返回EOF
    
    // sem 指向要初始化的信号量对象
    // pshared 信号量的使用范围 0代表线程间 1代表进程间
    // val信号量的初值,0代表没有资源,大于0代表有资源

    P/V操作

    #include <semaphore.h>
    int sem_wait(sem_t *sem); // P操作
    int sem_post(sem_t *sem); // V操作
    
    // 成功时返回0
    // 失败时返回EOF
    // sem执行要操作的信号量对象

    Demo1:两个线程同步读写缓冲区(生产者、消费者问题)

    #include <pthread.h>
    #include <stdio.h>
    #include <zconf.h>
    #include <string.h>
    #include <semaphore.h>
    
    // 全局buf
    char buf[32];
    sem_t ser;
    sem_t sew;
    
    void *function(void *args) {
        while (1) {
            // 执行p操作
            sem_wait(&ser);
            printf("you enter %d charachtes
    ", strlen(buf));
            sem_post(&sew);
        }
    };
    
    int main(int argc, char *argv[]) {
        pthread_t a_thread;
        // 先初始化信号量 写和读
        if (sem_init(&ser, 0, 0) < 0) {
            perror("sem_init");
            return 0;
        }
        // 这里初始化为1
        if (sem_init(&sew, 0, 1) < 0) {
            perror("sem_init");
            return 0;
        }
        // 再创建线程
        if (pthread_create(&a_thread, NULL, function, NULL) != 0) {
            printf("no create");
            return 0;
        }
        printf("input quit to exit:
    ");
        do {
            // 每次往缓冲区写完一次操作后都将执行一次post v操作操作
            sem_wait(&sew);
            fgets(buf, 32, stdin);
            sem_post(&ser);
    
        } while (strncmp(buf, "quit", 4) != 0);
        {
            // 如果输入不是quit则继续循环
            return 0;
        }
    }

    互斥机制

    临界资源

    • 一次只允许一个任务(进程、现场)访问的共享资源

    临界区

    • 访问临界的代码

    互斥机制

    • mutex互斥锁
    • 任务访问临界资源前申请锁,访问完后释放锁

    初始化锁-pthread_mutex_init

    #include <pthread.h>
    int pthread_mutex_init(pthread_mutex_t *mutex,const pthread_mutexattr_t *attr);
    
    // 成功时返回0
    // 失败时返回错误码
    // mutex 指向要初始化的互斥锁对象
    // attr互斥锁属性,NULL表示缺省属性

    申请锁-pthread_mutex_lock

    #include <pthread.h>
    int pthread_mutex_lock(pthread_mutex_t * mutex);
    
    // 成功时返回0
    // 失败时返回错误码
    // 如果无法获得锁,任务阻塞
    // mutex 指向要初始化的互斥锁对象

    释放锁-pthread_mutex_unlock

    #include <pthread.h>
    int pthread_mutex_unlock(pthread_mutex_t *mutex);
    
    // 成功时返回0
    // 失败时返回错误码
    // 执行完临界区要即使释放
    // mutex指向要初始化的互斥锁对象

    Demo:

    #include <pthread.h>
    #include <stdio.h>
    #include <zconf.h>
    
    unsigned int value1, value2;
    pthread_mutex_t lock;
    int number = 0;
    
    void *function() {
    
        pthread_mutex_lock(&lock);
        puts("子线程进入");
        value1 = number++;
        value2 = ++number;
        sleep(1);
        puts("子线程截止");
        printf("value1: %d value2:%d
    ", value1, value2);
        pthread_mutex_unlock(&lock);
        return 0;
    }
    
    
    int main(int argc, char *argv[]) {
        pthread_t a_thread;
    
        // 初始化锁
        if (pthread_mutex_init(&lock, NULL) != 0) {
            perror("pthread_mutex_init");
            return 0;
        }
        // 创建线程
        if (pthread_create(&a_thread, NULL, function, NULL) != 0) {
            perror("pthread_create");
            return 0;
        }
        // 加锁
        pthread_mutex_lock(&lock);
        puts("主进程进入");
        value1 = number++;
        value2 = ++number;
        sleep(1);
        puts("主进程截止");
        printf("value1: %d value2:%d
    ", value1, value2);
        pthread_mutex_unlock(&lock);
        sleep(3);
    }
    Songzhibin
  • 相关阅读:
    集合的遍历和扩容原理
    最新鲜最详细的Android SDK下载安装及配置教程
    开启IIS Express可以调试X64项目
    C# 正则表达式大全
    Windows下SQLMAP的安装图解
    当前比较流行的前端框架2
    前端组件库1
    服务器Windows 登录 出现401 错误
    IPV6配置
    implicit和 explicit关键字
  • 原文地址:https://www.cnblogs.com/binHome/p/12850818.html
Copyright © 2011-2022 走看看