zoukankan      html  css  js  c++  java
  • 第十二章 并发编程

    第十二章 并发编程

    概述:如果逻辑控制流在时间上重叠,那么他们就是并发的

    应用级并发在其他情况下的应用

    • 访问慢速I/O设备
    • 与人交互
    • 通过推迟工作以降低延迟
    • 服务多个网络客户端
    • 在多核机器上进行并发计算

    12.1基于进程的并发编程

    构造并发程序最简单的方法就是用进程

    常用函数

    • fork
    • exec
    • waitpid

    原理

    在父进程中接受客户端连接请求,然后创建一个新的子进程来为每个新客户端提供服务。

    12.1.1基于进程的并发服务器

    关于服务器需要说明的地方、

    • 要包括一个sigchld处理程序,来回收僵死程序
    • 父子进程必须关闭他们各自的connfd
    • 直到父子进程的connfd都关闭了,到客户端的链接才会终止

    12.1.2关于进程的优劣

    1.优点:防止虚拟存储器被错误覆盖

    2.缺点:开销高,共享状态信息才需要IPC机制

    12.2基于I/O多路复用的并发编程

    使用I/O多路复用的并发编程的原因

    同时响应下面两个请求

    • 网络客户端发起连接请求
    • 用户在键盘上输入命令行

    原理

    就是使用select函数要求内核挂起进程,只有在一个或多个I/O事件发生后,才将控制返回给应用程序。

    12.2.1基于I/O多路复用的并发事件驱动服务器

    事件驱动程序:将逻辑流模型化为状态机。
    状态机:

    • 状态
    • 输入事件
    • 转移

    12.2.2 I/O多路复用技术的优劣

    1.优点

    • 相较基于进程的设计,给了程序员更多的对程序程序的控制
    • 运行在单一进程上下文中,所以每个逻辑流都可以访问该进程的全部地址空间,共享数据容易实现
    • 可以使用GDB调试
    • 高效
      2.缺点
    • 编码复杂
    • 不能充分利用多核处理器

    12.3基于线程的并发编程

    是以上两种方法的混合

    • 线程有内核自动调度
    • 多个线程运行在单一进程的上下文中

    12.3.2posix线程

    void *thread(void *vargp);
    int main()
    {
       pthread_t tid;
       Pthread_create(&tid, NULL, thread, NULL);
       Pthread_join(tid,NULL);
       exit(0);
    }
    
    void *thread(void *vargp)
    {
       printf(“Hello , world
    ”);
       return NULL;
    }
    
    

    12.3.3创建线程

    线程通过调用pthread_create函数来创建其它线程

    #include <pthread.h>
    typedef void *(func)(void *);
    
    int pthread_create(pthread_t *tid, pthread_attr_t *attr, func *f, void *arg);
    

    12.3.4终止线程

    • 顶层的线程例程返回
    • 调用pthread_exit函数
    • 如果主线程调用,会先等待所有其他对等线程终止,再终止主线程和整个进程,返回值为pthread_return
      某个对等线程调用Unix的exit函数,会终止进程与其相关线程
    • 另一个对等线程通过以当前线程ID作为参数调用pthread_cancle来终止当前线程

    12.4多线程程序中的共享变量

    一个变量是共享的,当且仅当多个线程引用这个变量的某个实例。

    12.5用信号量同步线程

    第一个 CreateSemaphore

    函数功能:创建信号量

    函数原型:

    HANDLE CreateSemaphore(

    LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,

    LONG lInitialCount,

    LONG lMaximumCount,

    LPCTSTR lpName

    );

    函数说明:

    第一个参数表示安全控制,一般直接传入NULL。

    第二个参数表示初始资源数量。

    第三个参数表示最大并发数量。

    第四个参数表示信号量的名称,传入NULL表示匿名信号量。

    第二个 OpenSemaphore

    函数功能:打开信号量

    函数原型:

    HANDLE OpenSemaphore(

    DWORD dwDesiredAccess,

    BOOL bInheritHandle,

    LPCTSTR lpName

    );

    函数说明:

    • 第一个参数表示访问权限,对一般传入SEMAPHORE_ALL_ACCESS。详细解释可以查看MSDN文档。

    • 第二个参数表示信号量句柄继承性,一般传入TRUE即可。

    • 第三个参数表示名称,不同进程中的各线程可以通过名称来确保它们访问同一个信号量。

    第三个 ReleaseSemaphore

    函数功能:递增信号量的当前资源计数

    函数原型:

    BOOL ReleaseSemaphore(

    HANDLE hSemaphore,

    LONG lReleaseCount,

    LPLONG lpPreviousCount

    );

    函数说明:

    • 第一个参数是信号量的句柄。

    • 第二个参数表示增加个数,必须大于0且不超过最大资源数量。

    • 第三个参数可以用来传出先前的资源计数,设为NULL表示不需要传出。

    由于信号量是内核对象,因此使用CloseHandle()就可以完成清理与销毁了。

    12.6使用线程提高并行性

    四类线程不安全函数:

    • 不保护共享变量的函数

    • 保持跨越多个调用的状态的函数

    • 返回指向静态变量的指针的函数

    • 调用线程不安全函数的函数

    可重入性:当函数被多个线程调用,不会引用任何共享数据

    显式可重入:所有函数参数都是传值传递,所有数据引用都是本地自动栈变量

    隐式可重入:显式可重入加上一些参数是引用传递(指向非共享数据的指针)

    竞争:当一个程序的正确性依赖于,一个线程要在另一个线程到达y点之前到达它的控制流中的x点,就会发生竞争

    线程化的程序必须对任何可行的轨迹线都正确工作

    死锁:一组线程被阻塞了,等待一个永远也不会为真的条件。

    死锁不总是可以预测的,而错误常常不可重复。

    学习心得

    这周的内容和操作系统的内容有相似的地方,都是从分析并发执行的程序的状况。操作系统对于进程的调用,
    进程的死锁情况的解决以及更多别的问题讲的更详细。但是本书更注重从代码的角度讲述并发编程的问题,所以通过
    学习这门课,使我从不同的角度了解了操作系统。

    参考文献:

    • 深入理解计算机系统第十二章
    • 20135202闫佳歆同学的博客
    • zhanghaodx082的专栏http://blog.csdn.net/zhanghaodx082/article/details/11932865
  • 相关阅读:
    简单明了的带你理解springboot原理和三大核心注解
    Spring Boot(一):入门篇
    【Mysql优化】聚簇索引与非聚簇索引概念
    Mysql索引原理与优化
    Mysql全文索引的使用
    索引的优缺点,如何创建索引
    184 01 Android 零基础入门 03 Java常用工具类03 Java字符串 02 String类 04 例:字符串与byte(即:字节)数组间的相互转换
    183 01 Android 零基础入门 03 Java常用工具类03 Java字符串 02 String类 03 String常用方法(下)
    182 01 Android 零基础入门 03 Java常用工具类03 Java字符串 02 String类 02 String常用方法(上)
    181 01 Android 零基础入门 03 Java常用工具类03 Java字符串 02 String类 01 String常用方法简介
  • 原文地址:https://www.cnblogs.com/5320zhq/p/5022644.html
Copyright © 2011-2022 走看看