带你学习多线程编程

线程概念

定义

线程就是进程内部的执行流,一个进程至少有一个线程,线程拥有自己的私有资源同时也会和进程共享资源。

线程独有的资源

  • 线程描述符
  • 寄存器
  • 线程栈
  • errno
  • 信号掩码
  • 实时调度策略

线程和进程共享的资源

  • 全局变量
  • 代码段
  • 文件描述符表
  • 进程ID和组ID
  • 每种信号的处理方式
  • 当前工作目录

    线程和进程的区别

  • 线程是资源调度的最小单位 ,进程时资源分配的最小单位
  • 进程是一次程序运行活动,线程是进程中的一个执行路径
  • 进程之间不能共享资源,而线程共享所在进程的地址空间和其它资源。同时线程还有自己的栈和栈指针,程序计数器等寄存器。
  • 进程有自己独立的地址空间,而线程没有,线程必须依赖于进程而存在。

线程的创建

#include <pthread.h>

int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);

参数:

  1. thread:返回线程的id
  2. attr:线程属性,可以手动设置(下文将详细描述线程属性)
  3. start_routine:线程执行的函数的函数地址
  4. arg:线程执行函数的参数
    返回值:
    成功返回0,失败返回错误码。

    线程的终止

    有三种方式终止一个线程。

  5. 用return返回。
  6. 用pthread_exit();
    #include <pthread.h>
    void pthread_exit(void *retval);

    参数:
    retval:保存线程退出码,<font color="#dd00dd">这个指针一定要是全局变量或者堆上开辟的。</font><br/>

  7. 用pthread_cancel()
    #include <pthread.h>
    int pthread_cancel(pthread_t thread);

    参数:
    thread:结束的线程ID(可以结束任意线程)
    返回值:
    成功返回0,失败返回错误码。
    线程的终止不能用exit(),这是进程的终止方式。

线程的等待与分离

为什么需要线程等待

  • 不等待,线程结束后不会自动释放资源。
  • 会导致过多线程描述符被占用,无法创建新的线程。

    线程等待函数

    #include <pthread.h>
    int pthread_join(pthread_t thread, void **retval);

    参数:
    thread:等待线程的ID
    retval:保存退出状态码
    返回值:
    成功返回0,失败返回错误码。

    线程分离

    当我们不关心线程的退出状态,只希望线程结束系统会自动清理和释放资源,这时我们就可以使用线程分离。

    #include <pthread.h>
    int pthread_detach(pthread_t thread);

    参数:
    thread:分离的线程ID
    返回值:
    成功返回0,失败返回错误码。
    线程一旦被pthread_detach()分离,就不能再用pthread_join()获取其状态了,也不能再返回链接状态。

    线程属性

    在前面pthread_create()函数时,就涉及到pthread_attr_t *attr线程属性,我们可以手动设置线程属性。

    int pthread_attr_init(pthread_attr_t *attr);//初始化线程属性
    int pthread_attr_destroy(pthread_attr_t *attr);//销毁线程属性对象
    Detach state        = PTHREAD_CREATE_JOINABLE//分离属性
    int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);//设置分离属性
    int pthread_attr_getdetachstate(pthread_attr_t *attr, int *detachstate);//获得分离属性
    Scope               = PTHREAD_SCOPE_SYSTEM//抢夺资源范围
    Inherit scheduler   = PTHREAD_INHERIT_SCHED//是否继承线程调度策略
    Scheduling policy   = SCHED_OTHER//调度策略
    Scheduling priority = 0//调度优先级
    Guard size          = 4096 bytes//线程栈之间的安全区
    Stack address       = 0x40196000//自己指定线程栈
    Stack size          = 0x201000 bytes//栈的大小

    线程属性这一块,本文只是简单列出,更深层次的掌握需要大家自行查阅资料。

    线程的优缺点

    线程的优点

  • 创建一个线程的代价要比创建一个进程的代价小得多。
  • 与进程之间的切换相比较,线程之间的切换操作系统要做的工作少得多。
  • 线程占用的资源比进程少得多。
  • 能充分利用 多处理器的可并行数量。
  • 在等待慢速I/O操作结束时,程序可执行其他的计算任务。
  • 计算密集型应用,多处理器运行,可以将计算分布到多个线程中计算。
  • I/O密集型应用,为了提高性能,将I/O操作重叠,线程可以等待不同的I/O操作。

    线程的缺点

  • 性能损失
  • 健壮性降低。
  • 缺乏访问控制
  • 编程难度提高
  • 多线程对GDB支持不好
  • 多线程对信号支持不好

    线程互斥

    互斥量

    互斥量

    1. 定义互斥锁:pthread_mutex_t mutex;
    2. 初始化:pthread_mutex_init(&mutex,NULL);
    3. 上锁:pthread_mutex_lock(&mutex);
    4. 解锁:pthread_mutex_unlock(&mutex);
    5. 销毁:pthread_mutex_destroy(&mutex);

      自旋锁

    6. 定义自旋锁:pthread_spinlock_t spin;
    7. 初始化:int pthread_spin_init(pthread_spinlock_t *lock, int pshared);
    8. 上锁:int pthread_spin_lock(pthread_spinlock_t *lock);
    9. 解锁:int pthread_spin_unock(pthread_spinlock_t *lock);
    10. 销毁锁:int pthread_spin_destroy(pthread_spinlock_t *lock);

自旋锁与互斥锁的区别

自旋锁和互斥所的区别:互斥锁是当阻在pthread_mutex_lock时,放弃CPU,好让别人使用CPU。自旋锁阻塞在spin_lock时,不会释放CPU,不断的问CPU可以使用了不

读写锁

  1. pthread_rwlock_t lock;
  2. 初始化:int pthread_rwlock_init(pthread_rwlock_t restrict rwlock,const pthread_rwlockattr_t restrict attr);
  3. 读锁:int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
  4. 写锁:int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
  5. 解锁:int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
  6. 销毁锁:int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);

读读共享,读写排他,写写排他,写锁优先.

同步

条件变量

  1. 定义条件变量:pthread_cond_t cond;
  2. 初始化条件变量:int pthread_cond_init(pthread_cond_t restrict cond,const pthread_condattr_t restrict attr);
  3. 等待条件:int pthread_cond_wait(pthread_cond_t restrict cond,pthread_mutex_t restrict mutex);
        如果没有在锁环境下使用,互斥量形同虚设
        如果在锁环境下,将mutex解锁
        wait返回时,将mutex置为原来的状态
  4. 使条件满足:int pthread_cond_signal(pthread_cond_t *cond);
  5. 销毁条件变量:int pthread_cond_destroy(pthread_cond_t *cond);

综合案例

pthread_mutex_t mutex;//创建互斥量
int a = 0;
int b = 0;
void *r1(void* arg) //线程1执行函数
{
    while(1)
    {
        pthread_mutex_lock(&mutex);//上锁
        a++;
        b++;
        if(a != b)
        {
            printf("%d != %d\n",a,b);
        }
        pthread_mutex_unlock(&mutex);//解锁
    }
}

void *r2(void* arg)//线程2执行函数
{
    while(1)
    {
        pthread_mutex_lock(&mutex);
        a++;
        b++;
        if(a != b)
        {
            printf("%d != %d\n",a,b);
        }
        pthread_mutex_unlock(&mutex);
    }
}
int main(void)
{
    pthread_t t1,t2;
    pthread_mutex_init(&mutex,NULL);//初始化互斥量
    pthread_create(&t1,NULL,r1,NULL);//创建线程
    pthread_create(&t2,NULL,r2,NULL);//创建线程

    pthread_join(t1,NULL);//线程等待
    pthread_join(t2,NULL);
    return 0;
}

原文地址:http://blog.51cto.com/13449864/2119632

时间: 2024-07-30 21:49:48

带你学习多线程编程的相关文章

程序大牛由浅入深,带你学习面向对象编程

前言 面向对象的Java语言具备“一次编程,任何地方均可运行”的能力,使其成为服务提供商和系统集成商用以支持多种操作系统和硬件平台的首选解决方案.Java作为软件开发的一种革命性的技术,其地位已被确定.如今,Java 技术已被列为当今世界信息技术的主流之一. 正文 面向对象开发方法概述 一般说来,软件开发都会经历以下生命周期: ●软件分析:分析问题领域,了解用户的需求. ●软件设计:确定软件的总体架构,把整个软件系统划分成大大小小的多个子系统,设计每个子系统的具体结构. ●软件编码: 用选定的编

VC++多线程编程

一.问题的提出 编写一个耗时的单线程程序: 新建一个基于对话框的应用程序SingleThread,在主对话框IDD_SINGLETHREAD_DIALOG添加一个按钮,ID为IDC_SLEEP_SIX_SECOND,标题为“延时6秒”,添加按钮的响应函数,代码如下: void CSingleThreadDlg::OnSleepSixSecond() { Sleep(6000); //延时6秒 } 编译并运行应用程序,单击“延时6秒”按钮,你就会发现在这6秒期间程序就象“死机”一样,不在响应其它消

VC++ 多线程编程,win32,MFC 例子(转)

一.问题的提出 编写一个耗时的单线程程序: 新建一个基于对话框的应用程序SingleThread,在主对话框IDD_SINGLETHREAD_DIALOG添加一个按钮,ID为IDC_SLEEP_SIX_SECOND,标题为“延时6秒”,添加按钮的响应函数,代码如下: void CSingleThreadDlg::OnSleepSixSecond() { Sleep(6000); //延时6秒 } 编译并运行应用程序,单击“延时6秒”按钮,你就会发现在这6秒期间程序就象“死机”一样,不在响应其它消

Siege——多线程编程最佳实例

在英语中,“Siege”意为围攻.包围.同时Siege也是一款使用纯C语言编写的开源WEB压测工具,适合在GNU/Linux上运行,并且具有较强的可移植性.之所以说它是多线程编程的最佳实例,主要原因是Siege的实现原理中大量运用了多线程的各种概念.Siege代码中用到了互斥锁.条件变量.线程池.线程信号等很多经典多线程操作,因此对于学习多线程编程也大有裨益.最近花了一些时间学习到了Siege的源代码,本文将介绍一下Siege压测工具的内部原理,主要供系统测试同学.以及学习多线程编程的同学们参考

多线程编程学习笔记——async和await(三)

接上文 多线程编程学习笔记——async和await(一) 接上文 多线程编程学习笔记——async和await(二) 五.   处理异步操作中的异常 本示例学习如何在异步函数中处理异常,学习如何对多个并行的异步操作使用await时聚合异常. 1.程序示例代码如下. using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks;

多线程编程学习总结(转载)

线程的概念和原理 为什么使用多线程? 为了更高效的完成任务和利用CPU资源,现在的操作系统设计为多任务操作系统,而多进程和多线程是实现多任务的方式. 什么是进程和线程? 进程是指一个内存中运行的应用程序,每个进程都有自己独立的一块内存空间,一个进程中可以启动多个线程.进程是OS分配资源的最小单位. 线程是指进程中的一个执行流程,一个进程中可以运行多个线程.线程总是属于某个进程,进程中的多个线程共享进程的内存.进程是OS调度的最小单位. 工作原理? 多线程是这样一种机制,它允许在程序中并发执行多个

pthread多线程编程的学习小结

pthread多线程编程的学习小结 程序员必上的开发者服务平台 —— DevStore pthread多线程编程整理 1 Introduction 不用介绍了吧… 2 Thread Concepts 1.     Thread由下面部分组成: a.     Thread ID b.     Stack c.     Policy d.     Signal mask e.     Errno f.      Thread-Specific Data 3 Thread Identification

Java多线程编程(学习笔记)

一.说明 周末抽空重新学习了下多线程,为了方便以后查阅,写下学习笔记. 有效利用多线程的关键是理解程序是并发执行而不是串行执行的.例如:程序中有两个子系统需要并发执行,这时候需要利用多线程编程. 通过多线程的使用,可以编写出非常高效的程序.但如果创建了太多的线程,程序执行的效率反而会降低. 同时上下文的切换开销也很重要,如果创建太多的线程,CPU花费在上下文的切换时间将对于执行程序的时间. 二.Java多线程编程 概念 在学习多线程时,我们应该首先明白另外一个概念. 进程:是计算机中的程序关于某

Android多线程编程之线程池学习篇(一)

Android多线程编程之线程池学习篇(一) 一.前言 Android应用开发中多线程编程应用比较广泛,而应用比较多的是ThreadPoolExecutor,AsyncTask,IntentService,HandlerThread,AsyncTaskLoader等,为了更详细的分析每一种实现方式,将单独成篇分析.后续篇章中可能涉及到线程池的知识,特此本篇分析为何使用线程池,如何使用线程池以及线程池的使用原理. 二.Thread Pool基础 进程代表一个运行中的程序,一个运行中的Android