多线程

并发的三种实现手段

多线程,io多路复用,多进程

竞争

多线程的程序必须对任何可行的轨迹线都正确运行

死锁

就是等待一个永远不可能为真的值
当禁止区重叠的时候,就可能出现这种情况

可以调整枷锁的顺序进而:


关于二元信号量的不死锁条件
给定所有互斥操作的一个全序,如果每个线程都是以一种顺序获得互斥锁并以相反的顺序释放,那么这个程序就是无死锁的

线程安全函数

一个函数被称为线程安全的,当且仅当被多个并发线程反复调用时,他会一直产生正确的结果
典型的四类不安全的线程函数特点:
1.不保护共享变量的函数
2.保持跨越多个调用的状态的函数,如随机数生成器或者有局部静态变量
3.返回指向静态变量的指针的函数,如ctime,解决方法是加一个间接层拷贝数据,使之不返回指向静态变量的指针
4.调用线程不安全函数的函数

可重入性

没太明白意义
malloc,printf都是不可重入函数
可重入的函数必须满足以下三个条件:

(1)可以在执行的过程中可以被打断;

(2)被打断之后,在该函数一次调用执行完之前,可以再次被调用(或进入,reentered)。

(3)再次调用执行完之后,被打断的上次调用可以继续恢复执行,并正确执行。

获取线程返回值

pthread_join阻塞线程的同时获取返回值

pthread_exit = 单线程的exit,仅退出本线程,特别是pthread_exit主线程的时候,子线程不会被强制退出
pthread_kill(id,0)可以判断线程是否在运行

#include <iostream>
#include <unistd.h>
#include <pthread.h>
#include <signal.h>
using namespace std;

void* returnVal(void *arg){
    sleep(5);
    return new int(100);
}

int main(){
    pthread_t tid;
    void *returnValue;
    if (pthread_create(&tid,NULL,returnVal,NULL)){
       cout << "pthread_thread filed" << endl; 
       return 0;
    }   
    else{
        auto check = 
            [=]() -> bool{
                int tmp =  pthread_kill(tid,0);
                if (tmp == ESRCH){
                    return 0;
                }   
                else if (tmp == EINVAL){
                    cout << "debug+  " << "kill failed" << endl;
                    exit(0);
                }   
                else{
                    return 1;
                }   
            };  

        cout << check() << endl;
        pthread_join(tid,&returnValue);
        cout << "result = " << *static_cast<int*>(returnValue) << endl;
        cout << check() << endl;
        return 0;
    }

互斥的手段

信号量

在某些系统上,信号量是唯一可重入的互斥手段,消耗比较大

#include <stdio.h>
#include <semaphore.h>
#include <thread>
volatile int cnt = 0;
sem_t mutex;
void* thread(void *args){
    int niters = *((long*)args);
    for (int i = 0;i < niters;++i){
        sem_wait(&mutex);
        cnt++;
        sem_post(&mutex);
    }

    return NULL;
}
int main(int argc,char **argv){
    if (argc != 2){
        printf("usage: %s <niters>\n",argv[0]);
        exit(0);
    }
    else{
        sem_init(&mutex,0,1);
        int niters = atoi(argv[1]);
        pthread_t tid1,tid2,tid3;
        pthread_create(&tid1,NULL,thread,&niters);
        pthread_create(&tid2,NULL,thread,&niters);
        pthread_create(&tid3,NULL,thread,&niters);
        pthread_join(tid1,NULL);
        pthread_join(tid2,NULL);
        pthread_join(tid3,NULL);
        if (cnt != 2 * (niters)){
            printf("BOOM! cnt = %d\n",cnt);
        }
        else{
            printf("OK cnt = %d\n",cnt);
        }
    }
    return 0;
}

互斥锁

简化版的信号量

spin lock

原子操作

2.6版本之后的linux不直接支持原子操作,要通过汇编实现

include <stdio.h>
#include <semaphore.h>
#include <thread>
#include <atomic>

using namespace std;

std::atomic<int>  cnt;

void* thread1(void *args){
    int niters = *((long*)args);
    for (int i = 0;i < niters;++i){
        ++cnt;
    }

    return NULL;
}
int main(int argc,char **argv){
    if (argc != 2){
        printf("usage: %s <niters>\n",argv[0]);
        exit(0);
    }
    else{
        int niters = atoi(argv[1]);
        pthread_t tid1,tid2,tid3;
        pthread_create(&tid1,NULL,thread1,&niters);
        pthread_create(&tid2,NULL,thread1,&niters);
        pthread_create(&tid3,NULL,thread1,&niters);
        pthread_join(tid1,NULL);
        pthread_join(tid2,NULL);
        pthread_join(tid3,NULL);
        if (cnt.load() != 3 * (niters)){
            printf("BOOM! cnt = %d\n",cnt.load());
        }
        else{
            printf("OK cnt = %d\n",cnt.load());
        }
    }
    return 0;
}

条件变量

include 编译失败

g++ -odemo main.cpp -Wl,--no-as-needed -std=c++11 -lpthread
如果上述失败,试试这个
g++ -odemo -pthread test.c
-lpthread连接的仅仅是是libpthread, 但是-pthread连接的是libpthread和其他很多东西。

posted @ 2022-04-03 23:22  XDU18清欢  阅读(19)  评论(0)    收藏  举报