Linux的线程及线程间的通信
线程与进程的区别:
1.进程运行的时候,要分配空间,用来保护它的数据段,代码段还有堆和栈等,而线程的空间是共享的,多个线程运行的时候不会开辟新的空间,这样就效率快很多。
2.相对多进程来说,多线程是一种非常"节俭"的多任务操作方式。
线程的优点:
1.省内存,启动一个线程所花费的空间远远小于启动一个进程所花费的空间,而且,线程间彼此切换所需的时间也远远小于进程间切换所需要的时间。
2.进程间通信不方便,比较耗内存,而线程是用共享内存的,数据改变直接读取共享内存就知道,当然,数据共享也会带来一些问题,但可以用条件或者锁来控制。
线程间的通信方式
多线程开发的最基本概念主要包含三点:线程,互斥锁,条件。
线程操作有3种操作:线程的创建,退出,等待 。
互斥锁则包括 4 种操作,分别是创建,销毁,加锁和解锁。
条件操作有 5 种操作:创建,销毁,触发,广播和等待。
一.线程
**1. 线程创建**
#include <pthread.h>
int pthread_create(pthread_t *restrict tidp, const pthread_attr_t *restrict attr, void *(*start_rtn)(void *), void *restrict arg);
// 返回值:若成功返回0,否则返回错误编号
2. 线程退出
单个线程可以通过以下三种方式退出,在不终止整个进程的情况下停止它的控制流:
1)线程只是从启动例程中返回,返回值是线程的退出码。
2)线程可以被同一进程中的其他线程取消。
3)线程调用pthread_exit:
#include <pthread.h>
int pthread_exit(void *rval_ptr);
rval_ptr是一个无类型指针,与传给启动例程的单个参数类似。进程中的其他线程可以通过调用pthread_join函数访问到这个指针。
3. 线程等待
#include <pthread.h>
int pthread_join(pthread_t thread, void **rval_ptr);
// 返回:若成功返回0,否则返回错误编号
代码如下:
#include<stdio.h>
#include<pthread.h>
int data = 0;
void *fucn1(void *arg)
{
printf("t1: thread id is %ld\n",(unsigned long)pthread_self());
printf("t1: param is %d\n",*(int *)arg);
while(1)
{
printf("t1 :%d\n",data++);
sleep(1);
}
}
void *fucn2(void *arg)
{
printf("t2: thread id is %ld\n",(unsigned long)pthread_self());
printf("t2: param is %d\n",*(int *)arg);
while(1)
{
printf("t2 :%d\n",data++);
sleep(1);
}
}
int main()
{
int ret;
int param = 997;
pthread_t t1;
pthread_t t2;
ret = pthread_create(&t1,NULL,fucn1,(void *)¶m);
if(ret == 0)
{
printf("main:create t1 success\n");
}
ret = pthread_create(&t2,NULL,fucn2,(void *)¶m);
if(ret == 0)
{
printf("main:create t2 success\n");
}
printf("main: thread id is %ld\n",(unsigned long)pthread_self());
while(1)
{
printf("main :%d\n",data++);
sleep(1);
}
pthread_join(t1,NULL);
pthread_join(t2,NULL);
return 0;
}
二.互斥锁
1. 创建及销毁互斥锁
#include <pthread.h>
int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
int pthread_mutex_destroy(pthread_mutex_t mutex);
// 返回:若成功返回0,否则返回错误编号
要用默认的属性初始化互斥量,只需把attr设置为NULL。
2. 加锁及解锁
#include <pthread.h>
int pthread_mutex_lock(pthread_mutex_t mutex);
int pthread_mutex_trylock(pthread_mutex_t mutex);
int pthread_mutex_unlock(pthread_mutex_t mutex);
// 返回:若成功返回0,否则返回错误编号
注意:使用多把锁的时候,防止造成死锁
两个线程同时拿两把锁的时候就容易造成死锁。
代码如下:
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<pthread.h>
int data = 0;
pthread_mutex_t mutex;
void *fucn1(void *arg)
{
int i;
printf("t1: thread id is %ld\n",(unsigned long)pthread_self());
printf("t1: param is %d\n",*(int *)arg);
pthread_mutex_lock(&mutex);
while(1)
{
printf("t1 :%d\n",data++);
sleep(1);
if(data == 3)
{
pthread_mutex_unlock(&mutex);
exit(0);
}
}
}
void *fucn2(void *arg)
{
printf("t2: thread id is %ld\n",(unsigned long)pthread_self());
printf("t2: param is %d\n",*(int *)arg);
while(1)
{
printf("t2 :%d\n",data);
pthread_mutex_lock(&mutex);
data++;
pthread_mutex_unlock(&mutex);
sleep(1);
}
}
int main()
{
int ret;
int param = 997;
pthread_t t1;
pthread_t t2;
pthread_mutex_init(&mutex,NULL);
ret = pthread_create(&t1,NULL,fucn1,(void *)¶m);
ret = pthread_create(&t2,NULL,fucn2,(void *)¶m);
while(1)
{
printf("main :%d\n",data);
sleep(1);
}
pthread_join(t1,NULL);
pthread_join(t2,NULL);
pthread_mutex_destroy(&mutex);
return 0;
}
三.条件变量
1. 创建及销毁条件变量
#include <pthread.h>
int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);
int pthread_cond_destroy(pthread_cond_t cond);
// 返回:若成功返回0,否则返回错误编号
除非需要创建一个非默认属性的条件变量,否则pthread_cont_init函数的attr参数可以设置为NULL。
2. 等待
#include <pthread.h>
int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);
int pthread_cond_timedwait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, cond struct timespec *restrict timeout);
// 返回:若成功返回0,否则返回错误编号
pthread_cond_wait等待条件变为真。如果在给定的时间内条件不能满足,那么会生成一个代表一个出错码的返回变量。传递给pthread_cond_wait的互斥量对条件进行保护,调用者把锁住的互斥量传给函数。函数把调用线程放到等待条件的线程列表上,然后对互斥量解锁,这两个操作都是原子操作。这样就关闭了条件检查和线程进入休眠状态等待条件改变这两个操作之间的时间通道,这样线程就不会错过条件的任何变化。pthread_cond_wait返回时,互斥量再次被锁住。
pthread_cond_timedwait函数的工作方式与pthread_cond_wait函数类似,只是多了一个timeout。timeout指定了等待的时间,它是通过timespec结构指定。
3. 触发
#include <pthread.h>
int pthread_cond_signal(pthread_cond_t cond);
int pthread_cond_broadcast(pthread_cond_t cond);
// 返回:若成功返回0,否则返回错误编号
代码如下:
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<pthread.h>
int data = 0;
pthread_mutex_t mutex;
pthread_cond_t cond;
void *fucn1(void *arg)
{
int i;
printf("t1: thread id is %ld\n",(unsigned long)pthread_self());
printf("t1: param is %d\n",*(int *)arg);
while(1)
{
pthread_cond_wait(&cond,&mutex);
printf("t1 run============================\n");
printf("t2 :%d\n",data);
data = 0;
sleep(1);
}
}
void *fucn2(void *arg)
{
printf("t2: thread id is %ld\n",(unsigned long)pthread_self());
printf("t2: param is %d\n",*(int *)arg);
while(1)
{
printf("t2 :%d\n",data);
pthread_mutex_lock(&mutex);
data++;
if(data == 3)
{
pthread_cond_signal(&cond);
}
pthread_mutex_unlock(&mutex);
sleep(1);
}
}
int main()
{
int ret;
int param = 997;
pthread_t t1;
pthread_t t2;
pthread_mutex_init(&mutex,NULL);
pthread_cond_init(&cond,NULL);
ret = pthread_create(&t1,NULL,fucn1,(void *)¶m);
ret = pthread_create(&t2,NULL,fucn2,(void *)¶m);
/*while(1)
{
printf("main :%d\n",data);
sleep(1);
}*/
pthread_join(t1,NULL);
pthread_join(t2,NULL);
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&cond);
return 0;
}