muduo库学习之深入了解多线程(七)

muduo库学习之深入了解多线程(七)

因为muduo本身是多线程的网络库,前面已经分析了部分核心内容,接下来会将重点放在多线程的协调处理上,在此之前先深入了解下多线程。

本文先了解多线程(理论篇),然后是POSIX PthreadAPI(以下内容都来自PthreadsAPI,如有兴趣可以直接阅读原文)。这里线程(thread)不得不提及进程(process),进程是程序执行时的一个实例,即它是程序已经执行到何种程度的数据结构的汇集。进程的目的就是担当分配系统资源(CPU时间、内存等)的基本单位。线程是进程的一个执行流,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。一个进程由几个线程组成(拥有很多相对独立的执行流的用户程序共享应用程序的大部分数据结构),线程与同属一个进程的其他的线程共享进程所拥有的全部资源。(转自,吴秦的Linux多线程编程 )­­­,以下为UNIX PROCESS的图解下图,


下图为THREADS WITHIN A UNIX PROCESS如下图,


同样转载PThreadAPI里对Thread的描述:

  • So, in summary, in the UNIX environment a thread:
    • Exists within a process and uses the process resources
    • Has its own independent flow of control as long as its parent process exists and the OS supports it
    • Duplicates only the essential resources it needs to be independently schedulable
    • May share the process resources with other threads that act equally independently (and dependently)
    • Dies if the parent process dies - or something similar
    • Is "lightweight" because most of the overhead has already been accomplished through the creation of its process.

总的来说,线程存在于进程,共享进程资源,拥有独立的流程控制。

之前总是隐约有进程比线程更消耗系统资源更低效,但从不知怎么去量化,在同样一篇文章里面发现了关于这方面的在测试数据(50000次fork/pthread_create 的时间消耗)下图,


以上测试数据一目了然,除了上面的原因另外还有数据处理,线程可以共享数据,通过互斥和条件变量来进行控制同步,进程只能通过通信方式达到目的,综上则能肯定为什么选择多线程而非多进程小结下,进程包含多个线程,线程的开销比进程小得多,交互容易得多。

接下来更进一步的整理,线程相关的API,根据PthreadAPI的描述“in the Pthreads API are then covered: Thread Management, Mutex Variables, and Condition Variables”,看似三方面的东西其实也就是两个问题,线程的管理和同步。以下根据程序的流程来进行梳理,首先希望创建线程必须引用线程相关的头文件,主要是<ptrhead.h>,其次链接的时候需要指定动态库下图,

接下来我们根据创建使用销毁流程来分析主要的API:

#include <pthread.h> int pthread_create(pthread_t *tid, const pthread_attr_t *attr, void *(*func) (void *), void *arg);int pthread_join (pthread_t tid, void ** status);pthread_t pthread_self (void);int pthread_detach (pthread_t tid);

pthread_create用于创建一个线程,成功返回0,否则返回Exxx(为正数)。

int pthread_create(pthread_t *tid, const pthread_attr_t *attr, void *(*func) (void *), void *arg);pthread_t *tid:线程id的类型为pthread_t,通常为无符号整型,当调用pthread_create成功时,通过*tid指针返回。const pthread_attr_t *attr:指定创建线程的属性,如线程优先级、初始栈大小、是否为守护进程等。可以使用NULL来使用默认值,通常情况下我们都是使用默认值。void *(*func) (void *):函数指针func,指定当新的线程创建之后,将执行的函数。void *arg:线程将执行的函数的参数。如果想传递多个参数,请将它们封装在一个结构体中。

pthread_join用于等待某个线程退出,成功返回0,否则返回Exxx(为正数)。


互斥变量,使用互斥锁(互斥)可以使线程按顺序执行

#include <pthread.h> int pthread_mutex_lock(pthread_mutex_t * mptr); int pthread_mutex_unlock(pthread_mutex_t * mptr); 


条件变量,使用条件变量可以以原子方式阻塞线程,直到某个特定条件为真为

int pthread_cond_wait(pthread_cond_t *cptr, pthread_mutex_t *mptr); int pthread_cond_signal(pthread_cond_t *cptr); 


代码示例(转自,吴秦的Linux多线程编程 ),

/* 是否熟悉POSIX多线程编程技术?如熟悉,编写程序完成如下功能:  1)有一int型全局变量g_Flag初始值为0;  2)在主线称中起动线程1,打印“this is thread1”,并将g_Flag设置为1  3)在主线称中启动线程2,打印“this is thread2”,并将g_Flag设置为2  4)线程序1需要在线程2退出后才能退出  5)主线程在检测到g_Flag从1变为2,或者从2变为1的时候退出   */#include<stdio.h>#include<stdlib.h>#include<pthread.h>#include<errno.h>#include<unistd.h>typedef void* (*fun)(void*);int g_Flag=0;static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;void* thread1(void*);void* thread2(void*);/* *  when program is started, a single thread is created, called the initial thread or main thread. *  Additional threads are created by pthread_create. *  So we just need to create two thread in main(). */int main(int argc, char** argv){printf("enter main");pthread_t tid1, tid2;int rc1=0, rc2=0;rc2 = pthread_create(&tid2, NULL, thread2, NULL);if(rc2 != 0)printf("%s: %d",__func__, strerror(rc2));rc1 = pthread_create(&tid1, NULL, thread1, &tid2);if(rc1 != 0)printf("%s: %d",__func__, strerror(rc1));pthread_cond_wait(&cond, &mutex);printf("leave main");exit(0);}/* * thread1() will be execute by thread1, after pthread_create() * it will set g_Flag = 1; */void* thread1(void* arg){printf("enter thread1");printf("this is thread1, g_Flag: %d, thread id is %u",g_Flag, (unsigned int)pthread_self());pthread_mutex_lock(&mutex);if(g_Flag == 2)pthread_cond_signal(&cond);g_Flag = 1;printf("this is thread1, g_Flag: %d, thread id is %u",g_Flag, (unsigned int)pthread_self());pthread_mutex_unlock(&mutex);pthread_join(*(pthread_t*)arg, NULL);printf("leave thread1");pthread_exit(0);}/* * thread2() will be execute by thread2, after pthread_create() * it will set g_Flag = 2; */void* thread2(void* arg){printf("enter thread2");printf("this is thread2, g_Flag: %d, thread id is %u",g_Flag, (unsigned int)pthread_self());pthread_mutex_lock(&mutex);if(g_Flag == 1)pthread_cond_signal(&cond);g_Flag = 2;printf("this is thread2, g_Flag: %d, thread id is %u",g_Flag, (unsigned int)pthread_self());pthread_mutex_unlock(&mutex);printf("leave thread2");pthread_exit(0);}

推荐阅读