标签:share printf 执行顺序 怎么 确定调用 shared 多线程并发 不同的 自己
pthread线程知识要点线程之间通信的两个基本问题是互斥和同步。`
#include<pthread.h>
一、pthread_create
1、函数原型
int pthread_create(pthread_t tidp,const pthread_attr_t attr,(void)(start_rtn)(void),void arg);
2、函数功能:
pthread_create是类Unix操作系统(Unix、Linux、Mac OS X等)的创建线程的函数。
它的功能是创建线程(实际上就是确定调用该线程函数的入口点),在线程创建以后,就开始运行相关的线程函数。
返回成功时,由tidp指向的内存单元被设置为新创建线程的线程ID。attr参数用于指定各种不同的线程属性。
新创建的线程从start_rtn函数的地址开始运行,该函数只有一个万能指针参数arg,
如果需要向start_rtn函数传递的参数不止一个,那么需要把这些参数放到一个结构中,然后把这个结构的地址作为arg的参数传入。
3、返回值:
表示成功,返回0;表示出错,返回-1。
4、参数
第一个参数为指向线程标识符的指针。
第二个参数用来设置线程属性。
第三个参数是线程运行函数的起始地址。
最后一个参数是运行函数的参数。
示例:
// 线程的运行函数
void say_hello(void args)
{
cout << "Hello Runoob!" << endl;
return 0;
}
int main()
{
int indexes = 10;
pthread_t tids;
int ret = pthread_create(&tids, NULL, say_hello, (void *)&(indexe));
pthread_exit(NULL);
}
二、pthread_join
1、函数定义:
int pthread_join(pthread_t thread, void **retval);
2、函数功能:
pthread_join() 子程序阻碍调用程序,直到指定的 threadid 线程终止为止
三、线程存在的问题和临界区
前面我们知道了怎么创建线程,下面我们再来看看这样一个实例,创建100个线程,它们都访问了同一变量,
其中一半对这个变量进行加1操作,一半进行减1操作,按道理其结果会等于0.
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>
#define NUM_THREAD 100
void * thread_inc(void * arg);
void * thread_des(void * arg);
long long num = 0; //long long类型是64位整数型,多线程共同访问
int main(int argc, char *argv[])
{
pthread_t thread_id[NUM_THREAD];
int i;
//创建100个线程,一半执行thread_inc,一半执行thread_des
for(i = 0; i < NUM_THREAD; i++)
{
if(i %2)
pthread_create(&(thread_id[i]), NULL, thread_inc, NULL);
else
pthread_create(&(thread_id[i]), NULL, thread_des, NULL);
}
//等待线程返回
for (i = 0; i < NUM_THREAD; i++)
pthread_join(thread_id[i], NULL);
printf("result: %lld \n", num); //+1,-1按道理结果是0
return 0;
}
//线程入口函数1
void * thread_inc(void * arg)
{
for (int i = 0; i < 50000000; i++)
num += 1;//临界区(引起问题的语句就是临界区位置)
return NULL;
}
//线程入口函数2
void * thread_des(void * arg)
{
for (int i = 0; i < 50000000; i++)
num -= 1;//临界区
return NULL;
}
从运行结果看并不是0,而且每次运行的结果都不同。那这是什么原因引起的呢?
是因为每个线程访问一个变量是这样一个过程:先从内存取出这个变量值到CPU,
然后CPU计算得到改变后的值,最后再将这个改变后的值写回内存。因此,我们可以很容易看出,
多个线程访问同一变量,如果某个线程还只刚从内存取出数据,还没来得及写回内存,这时其它线程又访问了这个变量,所以这个值就会不正确了。
3.1、接下来我们再来讲讲怎么解决这个问题:线程同步
互斥量和信号量。
互斥:
互斥量技术从字面也可以理解,就是临界区有线程访问,其它线程就得排队等待,它们的访问是互斥的,实现方式就是给临界区加锁与释放锁。
#include <pthread.h>
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr); //创建互斥量
int pthread_mutex_destroy(pthread_mutex_t *mutex);//销毁互斥量
int pthread_mutex_lock(pthread_mutex_t *mutex);//加锁
int pthread_mutex_unlock(pthread_mutex_t *mutex);//释放锁
临界区围住一定要lock和unlock一一对应。
/*扩展临界区,减少加锁,释放锁调用次数,但这样变量必须加满到50000000次后其它线程才能访问.
这样是延长了线程的等待时间,但缩短了加锁,释放锁函数调用的时间,这里没有定论,自己酌情考虑*/
void * thread_inc(void * arg)
{
pthread_mutex_lock(&mutex); //互斥量锁住
for (int i = 0; i < 1000000; i++)
num += 1;
pthread_mutex_unlock(&mutex); //互斥量释放锁
return NULL;
}
/*缩短了线程等待时间,但循环创建,释放锁函数调用时间增加*/
void * thread_des(void * arg)
{
for (int i = 0; i < 1000000; i++)
{
pthread_mutex_lock(&mutex);
num -= 1;
pthread_mutex_unlock(&mutex);
}
return NULL;
}
信号量:
号量与互斥量类似,只是互斥量是用锁来控制线程访问而信号量是用二进制0,1来完成控制线程顺序。
sem_post信号量加1,sem_wait信号量减1,当信号量为0时,sem_wait就会阻断,因此通过这样让信号量加1减1就能控制线程的执行顺序了。
#include <semaphore.h>
int sem_init(sem_t *sem, int pshared, unsigned int value);//创建信号量
int sem_destroy(sem_t *sem);//销毁信号量
int sem_post(sem_t *sem);//信号量加1
int sem_wait(sem_t *sem);//信号量减1,为0时阻塞
#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>
void * read(void * arg);
void * accu(void * arg);
static sem_t sem_one;
static sem_t sem_two;
static int num;
int main(int argc, char *argv[])
{
pthread_t id_t1, id_t2;
sem_init(&sem_one, 0, 0);
sem_init(&sem_two, 0, 1);
pthread_create(&id_t1, NULL, read, NULL);
pthread_create(&id_t2, NULL, accu, NULL);
pthread_join(id_t1, NULL);
pthread_join(id_t2, NULL);
sem_destroy(&sem_one);
sem_destroy(&sem_two);
return 0;
}
void * read(void * arg)
{
int i;
for (i = 0; i < 5; i++) {
fputs("Input num: ", stdout);
sem_wait(&sem_two);
scanf("%d", &num);
sem_post(&sem_one);
}
return NULL;
}
void * accu(void * arg)
{
int sum = 0 , i;
for (i = 0; i < 5; i++) {
sem_wait(&sem_one);
sum+= num;
sem_post(&sem_two);
}
printf("Result: %d \n", sum);
return NULL;
}
四.多线程并发服务端的实现
标签:share printf 执行顺序 怎么 确定调用 shared 多线程并发 不同的 自己
原文地址:https://blog.51cto.com/wenxuehui/2433937