同步概念:
当多个线程共享相同的一块内存时(实际上在一个进程的各个线程之间,除了栈区的数据之外,其他的数据这几个县城之间都是相互共享的),需要确保每个线程看到一致的数据视图。也就是说,这些线程在对数据进行操作时,应该是同步的,也就是说当一个线程正在操作一个数据时,其他线程无法同时对该数据进行操作(读数据除外,因为读数据不会改动数据的内容)。这就是所谓的线程同步。
实现机制:
- 信号量。
信号量大致可以分为两种,一种取自POSIX的实时扩展,作用于线程。另外一种被称为系统V信号量,作用于进程的同步控制。信号量是一个特殊的变量,它可以被增加或者减少,但对其的关键访问被保证是原子操作,即使在一个多线程程序中也是如此,这意味着如果一个程序中有两个或者多个线程试图改变一个信号量的值,系统将保证所有的操作都将依次进行。
1.信号量的创建
这个函数初始化由sem指向的信号量对象,pshread参数控制信号量的类型,如果其值为0,就表示这个信号量只能当前进程局部使用,否则,这个信号量就可以在多个进程之间共享。但是现在的Linux系统花不支持这种共享,给pshared传递一个非0值将导致函数调用失败!
2.信号量的加减操作
sem_wait()函数表示对该信号量进行减操作(p操作),参数为信号量的id所在的地址。
sem_post()函数表示对该信号量进行加操作(v操作),参数为信号量的id所在的地址。
3.信号量的销毁
和前面的函数相同,这个函数的参数也是信号量的id所在的地址。
-
互斥锁。pthread线程库有一个互斥接口,可以确保同意时间只有一个线程访问数据。互斥量从本质上说是一把锁,在访问共享资源前对互斥量进行加锁,访问完成后释放互斥量上的锁。对互斥量进行加锁后,其他任何试图再次对互斥量进行加锁的线程都会阻塞,直到当前线程释放该互斥锁。如果释放该互斥锁时有多个线程阻塞,所有阻塞的线程都会变成可运行状态,第一个变成可运行状态的先成功可以对该互斥量进行加锁,其他线程看到互斥量又被锁住,只能回去再次等待他重新变成可用的。在这种方式下,每次只有一个线程可以向前执行,这就保证了线程之间的同步,对该资源的访问就会变得有序起来。
1.互斥锁的实现
互斥变量用pthread_mutex_t数据类型来表示,在使用护持变量之前必须先对其进行初始化。可通过调用pthread_mutex_init函数进行初始化,在释放内存前需要调用pthread_mutex_destroy函数进行释放。
第一个参数就是我们申请的护持变量的地址,如果要使用默认的属性初始化互斥量,只需要把attr参数设置为NULL。而我们大多书情况下也都是这样做的。
2.给互斥量加锁,解锁
对互斥量进行加锁,需要调用pthread_mutex_lock函数,如果互斥量已经上锁,则调用该函数的线程将阻塞到互斥量被解锁。对互斥量进行解锁,要调用pthread_mutex_unlock.
如果线程不希望被阻塞,它可以使用pthread_mutex_trylock尝试对互斥量进行加锁,如果调用该函数时互斥量未被加锁,那么该函数将对互斥量进行加锁,不会阻塞并返回0,否则该函数调用失败,返回一个EBUSY的宏。
信号量示例:
使用两个信号量实现两个线程的交替打印。
/*************************************************************************
> File Name: main.c
> Created Time: Fri 26 Oct 2018 08:14:50 PM CST
************************************************************************/
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<assert.h>
#include<pthread.h>
#include<semaphore.h>
sem_t sem1;
sem_t sem2;
static int flag = 0;
void *func(void *ptr)
{
int i = 0;
for(; i < 5; i++)
{
sem_wait(&sem2);
printf("func running\n");
sem_post(&sem1);
}
flag = 1;
printf("func return\n");
sem_post(&sem1);
}
int main()
{
sem_init(&sem1, 0,1);
sem_init(&sem2,0,1);
pthread_t pth;
int err = pthread_create(&pth, NULL,func,NULL);
assert(err == 0 );
int i = 0;
for(; i < 8; i++)
{
if(flag == 0)
sem_wait(&sem1);
printf("main running\n");
if(flag == 0)
sem_post(&sem2);
}
pthread_exit(NULL);
return 0;
}
互斥锁示例:
同样实现交替打印;
/*************************************************************************
> File Name: main.c
> Created Time: Fri 26 Oct 2018 08:14:50 PM CST
************************************************************************/
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<assert.h>
#include<pthread.h>
#include<semaphore.h>
pthread_mutex_t mutex1;
pthread_mutex_t mutex2;
static int flag = 0;
void *func(void *ptr)
{
int i = 0;
for(; i < 5; i++)
{
pthread_mutex_lock(&mutex1);
printf("func running\n");
pthread_mutex_unlock(&mutex2);
}
flag = 1;
printf("func return\n");
pthread_mutex_unlock(&mutex2);
}
int main()
{
int err1 = pthread_mutex_init(&mutex1,NULL);
assert(err1 == 0);
int err2 = pthread_mutex_init(&mutex2,NULL);
assert(err2 == 0);
pthread_t pth;
int err = pthread_create(&pth, NULL,func,NULL);
assert(err == 0 );
int i = 0;
for(; i < 8; i++)
{
if(flag == 0)
pthread_mutex_lock(&mutex2);
printf("main running\n");
if(flag == 0)
pthread_mutex_unlock(&mutex1);
}
pthread_exit(NULL);
return 0;
}