linuxbingc(多线程)

Posted 月屯

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了linuxbingc(多线程)相关的知识,希望对你有一定的参考价值。

上一篇

目录标题

线程概念

曾经写的代码当中有没有线程呢

理解以前:
进程在内核当中就是一个task_struct,在该结构体当中的成员变量 pid被我们称之为进程号。
现在理解:
1.操作系统当中没有线程的概念,程序猿说的创建线程,本质上在1inux操作系统当中创建轻量级进程(lwp)所以轻量级进程等价线程。
有的!,曾经写的代码当中存在线程,就是执行mian函数的执行流,被称之为主线程;程序员创建的线程被称之为叫做“工作线程”
2. pid本质上是轻量级进程id,换句话说,就是线程ID

在task struct当中
pid_t pid; //轻量级进程id,也被称之为线程id,不同的线程拥有不同的pid
pid_t tgid; //轻量级进程组id,也被称之为进程id,一个进程当中的线程拥有相同的tgid

为什么进程概念的时候,说pid就是进程id?
因为主线程的pid和tgid相等

线程的共享与独有

独有:在进程虚拟地址空间的共享区当中
调用栈,寄存器,线程ID,errno,信号屏蔽字,调度优先级
共享:
文件描述符表,用户id,用户组id,信号处理方式,当前进程的工作目录

线程的优缺点:

优点:

  • 多线程的程序,拥有多个执行流,“合理使用,可以提高程序的运行效率多线
  • 程程序的线程切换比多进程程序快,付出的代价小(有些可以共享的数
    据(全局变量)就能在线程切换的时候,不进行切换)
  • 可以充分发挥多核CPU并行的优势
    计算密集型的程序,可以进行拆分,让不同的线程执行计算不一样的
    事情(1+2+3+……)
    I/0密集型的程序,可以进行拆分,让不同的线程执行不同的I/0操
    作,可以不用串型运行,提高程序运行效率
    缺点:
  • 编写代码的难度更加高
  • 代码的(稳定性)鲁棒性要求更加高线程数量并不是越多越好(滑稽吃鸡)
  • 缺乏访问控制,可能会导致程序产生二义性结果
  • 一个线程崩溃,会导致整个进程退出。(滑稽吃鸡)

线程控制

g++ $^ -o $@ -lpthread -g

线程创建

#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
void* m_thread_start(void* arg)
  int* a=(int*)arg;
printf("i am woker%d\\n",*a);

int  main()
pthread_t tid;
int i=5;
int ret=pthread_create(&tid,NULL,m_thread_start,(void*)&i);
printf("%d\\n",ret);
sleep(5);
  return 0;


改善

struct Data_i
    int i;
;

void* my_thread_start(void* arg)

    struct Data_i* p = (struct Data_i*)arg;
    while(1)
        printf("i am work thread!: %d\\n", p->i);
        sleep(1);
    
    delete p;



int main()

    for(int i = 0; i < 5; i++)

        struct Data_i* lp = new Data_i();
        lp->i = i;

        pthread_t tid;
        int ret = pthread_create(&tid, NULL, my_thread_start, (void*)lp);
        if(ret < 0)
            perror("pthread_create");
            return 0;
        
    

    while(1)
        printf("i am main thread\\n");
        sleep(1);
    

查看线程

 ps aux | grep my
 pstack 进程号

top -H -p 进程号

结论

测试入口函数的传参
结论1:不要传递临时变量给线程的入口函数
结论2:如果给线程入口函数传递了一个从堆上开辟的空间,让线程自行释放

线程终止


#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
void* start(void* arg)
  while(1)
    printf("i am child");
    sleep(1);
  



int main()
  pthread_t tid;
  //创建线程
  int ret=pthread_create(&tid,NULL,start,NULL);
  if(ret<0)
    perror("pthread_create error");
    return 0;
  
  //线程中止
  pthread_cancel(tid);

  //主线程不退出
  while(1)
    sleep(5);
  
  return 0;


获取线程标识符

void* my_thread_start(void* arg)
    int count  = 10;
    while(count--)
        printf("i am work thread\\n");
        sleep(1);
    

    pthread_cancel(pthread_self());

    while(1)
        printf("i am work thread-2\\n");
        sleep(1);
    


int main()
    //1.创建工作线程
    pthread_t tid;
    int ret = pthread_create(&tid, NULL, my_thread_start, NULL);
    if(ret < 0)
        perror("pthread_create");
        return 0;
    
    //3.主线程死循环不退出, 观察现象
    while(1)
        printf("i am main thread\\n");
        sleep(1);
    
    return 0;

线程等待

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
void* start(void* arg)
 int count=0;
  while(count<10)
    printf("i am child");
    sleep(1);
    count++;
  
  return NULL;


int main()
  pthread_t tid;
  //创建线程
  int ret=pthread_create(&tid,NULL,start,NULL);
  if(ret<0)
    perror("pthread_create error");
    return 0;
  
  pthread_join(tid,NULL);
  return 0;


线程分离

#include <stdio.h>
#include <unistd.h>
#include <pthread.h>

void* my_thread_start(void* arg)
    pthread_detach(pthread_self());
    (void)arg;
    int count = 50;
    while(count--)
        printf("i am work thread\\n");
        sleep(1);
    
    return NULL;


int main()
    //1.创建工作线程
    pthread_t tid;
    int ret = pthread_create(&tid, NULL, my_thread_start, NULL);
    if(ret < 0)
        perror("pthread_create");
        return 0;
    

    while(1)
        sleep(1);
    
    return 0;


线程不安全状态

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>

int g_ticket = 100000;

void* my_thread_start(void* arg)
    //修改全局变量
    while(g_ticket > 0)
        printf("i am %p, cout g_i val is %d\\n", pthread_self(), g_ticket);
        g_ticket--;
    
    return NULL;


int main()
    //1.创建线程
    //  两个工作线程修改全局变量
    pthread_t tid[2];
    for(int i = 0; i < 2; i++)
        int ret = pthread_create(&tid[i], NULL, my_thread_start, NULL);
        if(ret < 0)
            perror("pthread_create");
            return 0;
        
    


    //2. 主线程 (只要不退出就好)
    for(int i = 0; i < 2; i++)
        pthread_join(tid[i], NULL);
    
    return 0;

同步与互斥

互斥

互斥的要做的事情:控制线程的访问时序。当多个线程能够同时访问到临界资源的时候,有可能会导致线程执行的结果产生二义性。
而互斥就是要保证多个线程在访问同一个临界资源,执行临界区代码的时候(非原子性性操作(线程可以被打断)),控制访问时序。让一个线程独占临界资源执行完,再让另外一个独占执行;

互斥锁

原理
互斥锁的本质就是0/1计数器,计数器的取值只能为0或者1
计数器的值为1∶表示当前线程可以获取到互斥锁,从而去访问临界资源计数器的值为0︰表示当前线程不可以获取到互斥锁,从而不能访问临界资源

需要理解的是:并不是说线程不获取互斥锁不能访问临界资源,而是程序猿需要在代码当中用同一个互斥锁,去约束多个线程。

否则线程A加锁访问,线程B访问临界资源之前不加锁,那也约束不了线程B

信号量的计数器当中如何保证原子性

为什么计数器当中的值从0变成1,或者从1变成0是原子性的呢?
直接使用寄存器当中的值和计数器内存的值交换,而交换是一条汇编指令就可以完成的

加锁的时候:寄存器当中的值设置为(0)
第一种情况:计数器的值为1,说明锁空闲,没有被线程加锁
交换情况(画图)

第二种情况:计数器的值为0,说明锁忙碌,被其他线程加锁拿走
交换情况(画图)

解锁的时候:寄存器当中的值设置为(1)
计数器的值为0,需要解锁,进行一步交换。

问题

1.什么是线程不安全

  • 多个线程并发/并行运行的时候,会导致程序结果的二义性。
  • 假设有两个线程,线程A,线程B,有一个CPU,两个线程同时想对全局变量i进行加加,i的初始值为10;
    2.怎么解决(互斥锁)

加锁

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>

int g_ticket = 100;
pthread_mutex_t g_lock;

void* my_thread_start(void* arg)
    //修改全局变量
    pthread_mutex_lock(&g_lock);
    while(g_ticket > 0)
        printf("i am %p, cout g_i val is %d\\n", pthread_self(), g_ticket);
        g_ticket--;
    
    return NULL;


int main()
    //0.初始化互斥锁
    pthread_mutex_init(&g_lock, NULL);
    //1.创建线程
    //  两个工作线程修改全局变量
    pthread_t tid[2];
    for(int i = 0; i < 2; i++)
        int ret = pthread_create(&tid[i], NULL, my_thread_start, NULL);
        if(ret < 0)
            perror("pthread_create");
            return 0;
        
    


    //2. 主线程 (只要不退出就好)
    for(int i = 0; i < 2; i++)
        pthread_join(tid[i], NULL);
    
    return 0;


解锁

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>

int g_ticket = 100;
pthread_mutex_t g_lock;

void* my_thread_start(void* arg)
    //修改全局变量
    while(g_ticket>0)
    pthread_mutex_lock(&g_lock);
    if(g_ticket>0)
        printf("i am %p, cout g_i val is %d\\n", pthread_self(), g_ticket);
        g_ticket--;
    
    pthread_mutex_unlock(&g_lock);
    return NULL;


int main()
    //0.初始化互斥锁
    pthread_mutex_init(&g_lock, NULL);
    //1.创建线程
    //  两个工作线程修改全局变量
    int i;
    pthread_t tid[2];
    for( i= 0; i < 2; i++)
        int ret = pthread_create(&tid[i], NULL, my_thread_start, NULL);
        if(ret < 0)
            perror("pthread_create");
            return 0;
        
    


    //2. 主线程 (只要不退出就好)
    for( i = 0; i < 2; i++)
        pthread_join(tid[i], NULL);
    
    return 0;


销毁锁

同步

#include <stdio.h>
#include <unistd.h>
#include <pthread.h>

#define THREAD_COUNT 1

//代表碗: 0表示没有面, 1表示有面
int g_bowl = 0;

pthread_mutex_t g_lock;

void* eat_thread_start(void* arg)
    while(1)
        pthread_mutex_lock(&g_lock);
        if(g_bowl == 0)
            printf("我是吃面人, 碗里面没有面了, 我就不吃了....\\n");
            pthread_mutex_unlock(&g_lock);
            continue;
        
        printf("碗里面有面, 我可以吃 : %d\\n", g_bowl--);
        //printf("i am eat thread: eat %d\\n", g_bowl--);
        pthread_mutex_unlock(&g_lock);
    


void* make_thread_start(void* arg)
    while(1)
        pthread_mutex_lock(&g_lock);
        if(g_bowl == 1)
            printf("我是做面人, 碗里面有面呢, 我就不做了...\\n");
            pthread_mutex_unlock(&g_lock);
            continue;
        
        printf("碗里面没有面了, 我可以做面了, %d\\n", ++g_bowl);
        //printf("i am make thread, make %d\\n", ++g_bowl);
        pthread_mutex_unlock(&g_lock);
    


int main()
    //1.初始化互斥锁
    pthread_mutex_init(&g_lock, NULL);


    //2.创建吃面的线程 和 做面的线程
    pthread_t eat[THREAD_COUNT], make[THREAD_COUNT];
    for(int i = 0; i < THREAD_COUNT; i++)
        int ret = pthread_create(&eat[i], NULL, eat_thread_start, NULL);
        if(ret < 0)
            perror("pthread_create");
            return 0;
        

        ret = pthread_create(&make[i], NULL, make_thread_start, NULL);
        if(ret < 0)
            perror("pthread_create");
            return 0;
        
    


    //3.等

以上是关于linuxbingc(多线程)的主要内容,如果未能解决你的问题,请参考以下文章

linuxbingc(多线程)

多线程与循环队列

多线程相关------临界区CriticalSection

c++多线程问题

多线程程序的临界区

RT-Thread多线程导致的临界区问题