嵌入式Linux并发程序设计,线程,线程间通信--同步,线号量,信号量初始化sem_init(),P

2019-07-12 21:58发布

文章目录

1,线程间通信

  1. 线程共享同一进程的地址空间
  2. 优点:线程间通信很容易通过全局变量交换数据
  3. 缺点:多个线程访问共享数据时需要同步或互斥机制

2,线程通信–同步

  1. 同步(synchronization)指的是多个任务按照约定的先后次序相互配合完成一件事情
  2. 1968年,Edsgar Dijkstra基于信号量的概念提出了一种同步机制
  3. 由信号量来决定线程是继续运行还是阻塞等待

3,信号量(灯)

  1. 信号量代表某一类资源,其值表示系统中该资源的数量
  2. 信号量是一个受保护的变量,只能通过三种操作来访问
    ·初始化
    ·P操作(申请资源):当任务(比如线程)要访问某个资源的时候,因为任务不知道当前系统中有没有这个资源,所以该任务对代表此资源的信号量进行P操作(检查信号量的值):如果信号量的值大于0,任务继续执行,访问资源,如果当前信号量的值等于0,就代表没有资源,则任务阻塞,直到有资源为止。
    ·V操作(释放资源):如果当前任务不需要访问资源了,或者任务产生了一个资源,就要执行V操作(告诉系统,资源数增加了,系统就可以唤醒等待这些资源的任务了)

4,Posix信号量

  1. posix中定义了两类信号量:
    ·无名信号量(基于内存的信号量):仅内存中存在,没有实际的文件和信号量一一对应,主要用于进程内部线程之间通信(,也可以用于进程之间但不方便)
    ·有名信号量:可用于线程间通信,也可用于进程间通信
  2. pthread库常用的信号量操作函数如下:
    int sem_init(sem_t *sem, int pshared, unsigned int value);
    int sem_wait(sem_t *sem); // P操作
    int sem_post(sem_t *sem); // V操作

5,信号量初始化sem_init()

#include
int sem_init(sem_t *sem, int pshared, unsigned int val);
  1. 成功时返回0,失败时EOF
  2. sem 指向要初始化的信号量对象
  3. pshared 0 – 线程间 1 – 进程间
  4. val 信号量初值

6,信号量–P/V操作sem_wait()/sem_post()

  1. P(S) 含义如下:
if (信号量的值大于0) { 申请资源的任务继续运行; 信号量的值减一;} else { 申请资源的任务阻塞; }
  1. V(S) 含义如下:
信号量的值加一; if (有任务在等待资源) { 唤醒等待的任务,让其继续运行 } #include
int sem_wait(sem_t *sem); P操作
int sem_post(sem_t *sem); V操作 ·成功时返回0,失败时返回EOF
·sem 指向要操作的信号量对象

7,线程同步—示例1

两个线程同步读写缓冲区(生产者/消费者问题) #include #include #include #include #include char buf[32]; sem_t sem; void *function(void *arg); int main(void) { pthread_t a_thread; if (sem_init(&sem,0,0) < 0) //先初始化信号量,再创建线程 { perror("sem_init"); exit(-1); } if (pthread_create(&a_thread,NULL,function,NULL) != 0) //先初始化信号量,再创建线程 { printf("fail to pthread_create"); exit(-1); } printf("input ‘quit’ to exit "); do { fgets(buf,32,stdin); sem_post(&sem); }while (strncmp(buf,"quit",4) != 0); return 0; } void *function(void *arg) { while (1) { sem_wait(&sem); printf("you enter %d characters ", strlen(buf)); } }
查看线程要加"-L"
linux@linux:~/test/pthread$ ps aux -L |grep sem linux 2978 2978 0.0 1 0.0 14772 928 pts/11 T 15:05 0:00 vi semc.c linux 4104 4104 0.0 1 0.5 14636 5488 pts/11 S+ 16:53 0:00 vi sem.c linux 4463 4463 0.0 1 0.0 6112 852 pts/14 S+ 17:28 0:00 grep --color=auto sem linux@linux:~/test/pthread$ ./sem.out input ‘quit’ to exit
再打开一个终端
linux@linux:~/test/pthread$ ps aux -L |grep sem linux 2978 2978 0.0 1 0.0 14772 928 pts/11 T 15:05 0:00 vi semc.c linux 4104 4104 0.0 1 0.5 14636 5488 pts/11 S+ 16:53 0:00 vi sem.c linux 4464 4464 0.0 2 0.0 10476 308 pts/14 Sl+ 17:28 0:00 ./sem.out linux 4464 4465 0.0 2 0.0 10476 308 pts/14 Sl+ 17:28 0:00 ./sem.out linux 4473 4473 0.0 1 0.0 6112 856 pts/0 S+ 17:29 0:00 grep --color=auto sem
  1. 多了两个线程,线程号LWP分别是4464、4465
  2. 他们同属于一个进程,进程号PID是4464
  3. 两个线程都处于等待态
程序功能如下所示
linux@linux:~/test/pthread$ ./sem.out input ‘quit’ to exit qw you enter 3 characters q you enter 2 characters ^C linux@linux:~/test/pthread$

8,线程同步—示例2

两个线程同步读写缓冲区(生产者/消费者问题)
上面的示例并没有实现严格意义上的同步
  1. 读线程在读缓冲区前,P操作检查缓冲区中有没有数据,没有的话阻塞,有的话才读数据
  2. 实际上对于写线程来说,也要如此。当缓冲区为空的时候,才能去写数据。
  3. 如果读线程数据处理过程比较长,读线程还没读完,写线程又把新数据覆盖上去了,这样就破坏了数据,是不合理的
#include #include #include #include #include char buf[32]; sem_t sem_r,sem_w; void *function(void *arg); int main(void) { pthread_t a_thread; if (sem_init(&sem_r,0,0) < 0) //刚开始缓冲区是空的,不可读 { perror("sem_r_init"); exit(-1); } if (sem_init(&sem_w,0,1) < 0) //刚开始缓冲区是空的,可写 { perror("sem_r_init"); exit(-1); } if (pthread_create(&a_thread,NULL,function,NULL) != 0) //先初始化信号量,再创建线程 { printf("fail to pthread_create"); exit(-1); } printf("input ‘quit’ to exit "); do { sem_wait(&sem_w);//写之前对可写信号量进行P操作,缓冲区非空则阻塞 fgets(buf,32,stdin);//缓冲区可写,写线程执行写操作 sem_post(&sem_r); //写完之后对可读信号量进行V操作,表示可读信号量增加了 }while (strncmp(buf,"quit",4) != 0); return 0; } void *function(void *arg) { while (1) { sem_wait(&sem_r); printf("you enter %d characters ",strlen(buf)); sem_post(&sem_w); } }