pthread线程知识要点多线程
线程之间通讯的两个基本问题是互斥和同步。`并发
#include<pthread.h>ide
1、pthread_create
一、函数原型
int pthread_create(pthread_t tidp,const pthread_attr_t attr,(void)(start_rtn)(void),void arg);函数
二、函数功能:
pthread_create是类Unix操做系统(Unix、Linux、Mac OS X等)的建立线程的函数。
它的功能是建立线程(实际上就是肯定调用该线程函数的入口点),在线程建立之后,就开始运行相关的线程函数。post
返回成功时,由tidp指向的内存单元被设置为新建立线程的线程ID。attr参数用于指定各类不一样的线程属性。 新建立的线程从start_rtn函数的地址开始运行,该函数只有一个万能指针参数arg, 若是须要向start_rtn函数传递的参数不止一个,那么须要把这些参数放到一个结构中,而后把这个结构的地址做为arg的参数传入。
三、返回值:
表示成功,返回0;表示出错,返回-1。操作系统
四、参数
第一个参数为指向线程标识符的指针。
第二个参数用来设置线程属性。
第三个参数是线程运行函数的起始地址。
最后一个参数是运行函数的参数。线程
示例:
// 线程的运行函数
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); }
2、pthread_join
一、函数定义:
int pthread_join(pthread_t thread, void **retval);
二、函数功能:
pthread_join() 子程序阻碍调用程序,直到指定的 threadid 线程终止为止code
3、线程存在的问题和临界区
前面咱们知道了怎么建立线程,下面咱们再来看看这样一个实例,建立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.一、接下来咱们再来说讲怎么解决这个问题:线程同步 互斥量和信号量。 互斥: 互斥量技术从字面也能够理解,就是临界区有线程访问,其它线程就得排队等待,它们的访问是互斥的,实现方式就是给临界区加锁与释放锁。 #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; }
四.多线程并发服务端的实现