- 线程的概念
一个进程可以有许多线程;进程是线程的容器;线程的执行时并发的;同一进程的不同线程共享进程的数据段、代码段、堆区;但每个线程都有自己的栈段(从进程的栈区分出来的)、线程ID、寄存器等等;
使用return 或 exit退出了主进程,则其含有的线程也结束了;可以用pthread_exit退出主线程 或 用pthread_join函数等待相应线程的结束。 线程的创建
int pthread_create(pthread_t id, pthread_attr_t *attr, (void ) function, void *arg ); 入参分别为 线程id, 线程属性(一般为NULL), 线程函数指针,传给线程的参数;
线程的退出:pthread_exit( void * exit_code);
线程的阻塞等待:pthread_join(id,void ** exit_code);PS: 同步:在访问资源的时候,以某种特定顺序的方式去访问资源(你先访问还是我先访问) 互斥:一个资源每次只能被一个进程所访问 同步与互斥是保证在高效率运行的同时,可以正确运行。大部分情况下**同步是在互斥的基础上进行的**。
线程的同步方式有三种:互斥锁、条件变量、信号量。
三种实质上都是一种锁机制,通过创建全局变量(放在数据区,线程可以共享),调用来实现线程的同步与互斥
ps: IPC的消息队列、信号量、共享内存也可以看做是一种锁机制,但其通过ftok函数创建key键值(可以放在栈区),进程之间通过键值彼此关联起来,相互配合实现进程的通信(数据传输,同步与互斥) 。
一、互斥锁(mutex)
通过锁的机制实现线程间的同步。
1.初始化锁:Linux系统下,锁的数据类型为pthread_mutex_t,初始化分为两种方式。
静态分配: pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
动态分配: int pthread_mutex_init(pthread_mutex *mutex, const pthread_mutex_attr_t *mutexattr);
2.加锁:对共享资源的访问,需对互斥量加锁;如果互斥量已经上锁,调用进程会堵塞,直到被解锁。
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
3.解锁:完成了对资源的访问后,需对互斥量进行解锁。
int pthread_mutex_unlock(pthread_mutex_t *mutex)
4.销毁锁:使用完成后销毁锁已用来释放资源。
int pthread_mutex_destroy(pthread_mutex_t *mutex) ;
调用成功返回值为0;否则为非0的错误码;
参照网上的代码,对代码段进行加锁,如下:
**#include <stdio.h>
#include <pthread.h>
#include <unistd.h>**
pthread_mutex_t mutex; //互斥锁
// 打印机
void printer(char *str)
{
pthread_mutex_lock(&mutex); //上锁
while(*str!='\0')
{
putchar(*str);
fflush(stdout);
str++;
sleep(1);
}
printf("\n");
pthread_mutex_unlock(&mutex); //解锁
}
// 线程一
void *thread_fun_1(void *arg)
{
char *str = "hello";
printer(str); //打印
}
// 线程二
void *thread_fun_2(void *arg)
{
char *str = "world";
printer(str); //打印
}
int main(void)
{
pthread_t tid1, tid2;
pthread_mutex_init(&mutex, NULL); //初始化互斥锁
// 创建 2 个线程
pthread_create(&tid1, NULL, thread_fun_1, NULL);
pthread_create(&tid2, NULL, thread_fun_2, NULL);
// 等待线程结束,回收其资源
pthread_join(tid1, NULL);
pthread_join(tid2, NULL);
pthread_mutex_destroy(&mutex); //销毁互斥锁
return 0;
}
二、条件变量(cond):
和互斥锁不同的是条件变量是用来等待的,不是用来上锁的,一般情况下和锁共用;在等待条件变量的过程中,pthread_cond_wait(&cond,&mutex)会阻塞线程,并释放互斥锁;其它线程在完成功能,调用pthread_cond_signal(&cond),会激活调用条件变量cond的线程,并获得互斥锁。
1.初始化条件变量。
静态分配:pthread_cond_t cond = PTHREAD_COND_INITIALIER;
动态分配:int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr);
2.等待条件成立。释放锁,同时阻塞等待条件变量为真才行。timewait()设置等待时间,仍未signal,返回ETIMEOUT(加锁保证只有一个线程wait)
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
int pthread_cond_timewait(pthread_cond_t *cond,pthread_mutex *mutex,const timespec *abstime);
3.激活条件变量。pthread_cond_signal,pthread_cond_broadcast(激活所有等待线程)
int pthread_cond_signal(pthread_cond_t *cond);
int pthread_cond_broadcast(pthread_cond_t *cond); //解除所有线程的阻塞
4.清除条件变量。无线程等待,否则返回EBUSY
int pthread_cond_destroy(pthread_cond_t *cond);
三、信号量(sem)
线程用的信号量和进程间通信用的信号量概念是一样的;只有0和1数值得为二进制信号量,只能进行原子操作,主要用于保护一段代码在一段时间内只允许单个线程访问,一般以sem_函数开头,定义在semaphore.h头文件中;数据类型为sem_t;
1.信号量的初始化
int sem_init (sem_t *sem , int pshared, unsigned int value); 其中pshared为它的共享选项(linux 只支持为0,即表示它是当前进程的局部信号量),然后给它一个初始值VALUE(0 or 1)。
2.等待信号量
int sem_wait(sem_t *sem);等待并阻塞线程,直到sem信号量的值大于0,然后将值-1,执行下一步程序;
3.释放信号量
int sem_post(sem_t *sem);给sem值+1,并通知其它因等待而阻塞的线程可以运行啦;
4.销毁信号量
int sem_destroy(sem_t *sem). 信号量也是一种资源(可以理解为一种存放在数据区的全局变量),用完后需清理,归返占有的资源。
**#include <semaphore.h>**
#include <errno.h>
#define return_if_fail(p) if((p) == 0){printf ("[%s]:func error!/n", __func__);return;}
typedef struct _PrivInfo
{
sem_t s1;
sem_t s2;
time_t end_time;
}PrivInfo;
static void info_init (PrivInfo* thiz);
static void info_destroy (PrivInfo* thiz);
static void* pthread_func_1 (PrivInfo* thiz);
static void* pthread_func_2 (PrivInfo* thiz);
int main (int argc, char** argv)
{
pthread_t pt_1 = 0;
pthread_t pt_2 = 0;
int ret = 0;
PrivInfo* thiz = NULL;
thiz = (PrivInfo* )malloc (sizeof (PrivInfo));
if (thiz == NULL)
{
printf ("[%s]: Failed to malloc priv./n");
return -1;
}
info_init (thiz);
ret = pthread_create (&pt_1, NULL, (void*)pthread_func_1, thiz);
if (ret != 0)
{
perror ("pthread_1_create:");
}
ret = pthread_create (&pt_2, NULL, (void*)pthread_func_2, thiz);
if (ret != 0)
{
perror ("pthread_2_create:");
}
pthread_join (pt_1, NULL);
pthread_join (pt_2, NULL);
info_destroy (thiz);
return 0;
}
static void info_init (PrivInfo* thiz)
{
return_if_fail (thiz != NULL);
thiz->end_time = time(NULL) + 10;
sem_init (&thiz->s1, 0, 1);
sem_init (&thiz->s2, 0, 0);
return;
}
static void info_destroy (PrivInfo* thiz)
{
return_if_fail (thiz != NULL);
sem_destroy (&thiz->s1);
sem_destroy (&thiz->s2);
free (thiz);
thiz = NULL;
return;
}
static void* pthread_func_1 (PrivInfo* thiz)
{
return_if_fail(thiz != NULL);
while (time(NULL) < thiz->end_time)
{
sem_wait (&thiz->s2);
printf ("pthread1: pthread1 get the lock./n");
sem_post (&thiz->s1);
printf ("pthread1: pthread1 unlock/n");
sleep (1);
}
return;
}
static void* pthread_func_2 (PrivInfo* thiz)
{
return_if_fail (thiz != NULL);
while (time (NULL) < thiz->end_time)
{
sem_wait (&thiz->s1);
printf ("pthread2: pthread2 get the unlock./n");
sem_post (&thiz->s2);
printf ("pthread2: pthread2 unlock./n");
sleep (1);
}
return;
}