“多线程”简介及其C代码实现框架

简介: 在一些计算机专业相关的书籍中,大家经常听说“多线程”这个概念。那么什么是“多线程”?什么时候使用“多线程”?在程序设计中使用“多线程”有什么好处呢?很多刚入职的程序员也对“多线程”感到非常的好奇,认为它很“高大上”。

在一些计算机专业相关的书籍中,大家经常听说“多线程”这个概念。那么什么是“多线程”?什么时候使用“多线程”?在程序设计中使用“多线程”有什么好处呢?很多刚入职的程序员也对“多线程”感到非常的好奇,认为它很“高大上”。本文对“多线程”进行了简单的介绍,并给出了其C代码的实现框架。

“单线程”程序
要想理解“多线程”,那么就要先从“单线程”说起。
大家都知道工厂“流水线”作业,里面的工序是一环扣一环的,只有前面的一道工序完成之后,才能够启动下一道工序。这其实和“单线程”的原理非常的相似。
在“单线程”里面,程序的功能是顺序执行的,只有前面的流程都成功执行,后面的流程才能够被执行到。例如,要实现一个话单文件生成、上传和删除的程序,使用“单线程”程序来完成,那么其流程如图1所示。
这里写图片描述
图1 “单线程”程序

“多线程”程序
大家也许注意到了,图1中的生成文件、上传文件和删除文件的流程其实可以独立开来。也就是说,这三个流程是互不影响的。这样也就诞生了“多线程”的概念。
“多线程”,顾名思义,就是多个“单线程”,每个线程独立地完成相关的功能。如图1所示的程序,如果用“多线程”来实现,那么其流程如图2所示。
这里写图片描述
图2 “多线程”程序

从图2可以看出,当程序启动之后,线程1、线程2和线程3是同时运行的。线程1仅用于生成话单文件,线程2仅用于上传话单文件,线程3仅用于删除过期的话单文件。这样一来,任何一个线程执行成功与否对另外两个线程都没有影响,真正地实现了程序的“并行”。

“多线程”的优点
“多线程”在大型软件程序中有着很广泛的应用,其优点如下:
第一,将原来在一个大流程中实现的功能放到了多个小流程中,程序更加的简洁和易于阅读。
第二,将不同的功能放到不同的线程中,提高了程序的执行效率。
第三,“多线程”使得程序的模块化更强,有利于追踪程序执行过程和排查问题。

“多线程”的C代码框架

/**********************************************************************
* 版权所有 (C)2015, Zhou Zhaoxiong。
*
* 文件名称:ThreadCreate.c
* 文件标识:无
* 内容摘要:演示多线程的创建
* 其它说明:无
* 当前版本:V1.0
* 作    者:Zhou Zhaoxiong
* 完成日期:20151029
*
**********************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

// 重定义数据类型
typedef signed   int    INT32;
typedef unsigned int    UINT32;

// 宏定义
#define THREAD_NUM     5              // 线程个数

// 函数声明
void ScanTask(void *pParam);
void ProcessTask(void *pParam);


/**********************************************************************
* 功能描述:主函数
* 输入参数:无
* 输出参数:无
* 返 回 值:无
* 其它说明:无
* 修改日期        版本号     修改人            修改内容
* -------------------------------------------------------------------
* 20151029        V1.0   Zhou Zhaoxiong       创建
***********************************************************************/
INT32 main()
{
    pthread_t MultiHandle  = 0;      // 多线程句柄
    pthread_t SingleHandle = 0;     // 单线程句柄
    UINT32    iLoopFlag    = 0;
    INT32     iRetVal      = 0;  // 创建线程函数的返回值

    // 循环创建线程
    for (iLoopFlag = 0; iLoopFlag < THREAD_NUM; iLoopFlag ++)
    {
        iRetVal = pthread_create(&MultiHandle, NULL, (void * (*)(void *))(&ScanTask), (void *)iLoopFlag);
        if (0 != iRetVal)
        {
            printf("Create ScanTask %d failed!\n", iLoopFlag);
            return -1;
        }
    }

    // 单独创建线程
    iRetVal = pthread_create(&SingleHandle, NULL, (void * (*)(void *))(&ProcessTask), NULL);
    if (0 != iRetVal)
    {
        printf("Create ProcessTask failed!\n");
        return -1;
    }

    return 0;   
}


/**********************************************************************
 * 功能描述: 扫描线程
 * 输入参数: pParam-线程编号
 * 输出参数: 无
 * 返 回 值: 无
 * 其它说明: 无
 * 修改日期            版本号            修改人           修改内容
 * ----------------------------------------------------------------------
*  20151029           V1.0          Zhou Zhaoxiong        创建
 ************************************************************************/
void ScanTask(void *pParam)
{
    UINT32  iThreadNo   = 0;        // 线程编号

    iThreadNo = (UINT32)pParam;     // 获取线程编号

    printf("Now, into ScanTask[%d].\n", iThreadNo);    // 打印包含线程编号的消息

    // 进行后续操作
}


/**********************************************************************
 * 功能描述: 处理线程
 * 输入参数: 无
 * 输出参数: 无
 * 返 回 值: 无
 * 其它说明: 无
 * 修改日期            版本号            修改人           修改内容
 * ----------------------------------------------------------------------
*  20151029            V1.0          Zhou Zhaoxiong      创建
 ************************************************************************/
void ProcessTask(void *pParam)
{
    printf("Now, into ProcessTask.\n");

    // 进行后续操作
}

说明
第一,本程序利用pthread_create函数来创建线程,该函数的原型是:

int pthread_create(pthread_t tidp,const pthread_attr_t *attr,(void)(start_rtn)(void),void *arg);

第一个参数为指向线程标识符的指针,在本程序中为MultiHandle和SingleHandle。
第二个参数用来设置线程属性。
第三个参数是线程运行函数的起始地址,在本程序中即为函数名。
第四个参数是运行函数的参数,当同时创建多个功能相同的线程时,该参数表示线程编号。

第二,在Linux下,该程序的编译命令为:gcc -g -o ThreadCreate ThreadCreate.c –lpthread。注意,最后的“–lpthread”是不能省略的,否则程序编译不通过。因为pthread并非Linux系统的默认库,而要在Linux中将其作为一个库来使用,就需要加上“-lpthread”或“-pthread”以显式链接该库。
第三,在程序的多线程中,建议不要同时对同一个全局变量进行加、减等操作。如果确实需要这样做,要注意在关键代码处使用加锁操作。

总结
随着软件功能的增强,随之而来的就是程序复杂度的提升,这也使得程序从“单线程”到“多线程”的转变成为必然。
“多线程”和“单线程”分别对应“并行”和“串行”,是软件开发人员必须要掌握的一种程序设计的方法。设计合理的“多线程”程序不仅逻辑清晰、易于阅读,而且程序的执行效率高,对于软件产品效率和质量的提升具有很重要的意义。
最后,推荐大家阅读一篇文章《进程与线程的一个简单解释》(http://www.ruanyifeng.com/blog/2013/04/processes_and_threads.html),本文以图形的方式展示了进程与线程的区别,及有关操作系统的其它概念,值得一读。

原文链接:http://www.daxixiong.com/?/article/12

本文中的代码已提交到GitHub上,请见:https://github.com/zhouzxi/ThreadCreate

目录
相关文章
|
1月前
|
安全 Java 编译器
深入理解Java中synchronized三种使用方式:助您写出线程安全的代码
`synchronized` 是 Java 中的关键字,用于实现线程同步,确保多个线程互斥访问共享资源。它通过内置的监视器锁机制,防止多个线程同时执行被 `synchronized` 修饰的方法或代码块。`synchronized` 可以修饰非静态方法、静态方法和代码块,分别锁定实例对象、类对象或指定的对象。其底层原理基于 JVM 的指令和对象的监视器,JDK 1.6 后引入了偏向锁、轻量级锁等优化措施,提高了性能。
58 3
|
2月前
|
供应链 安全 NoSQL
PHP 互斥锁:如何确保代码的线程安全?
在多线程和高并发环境中,确保代码段互斥执行至关重要。本文介绍了 PHP 互斥锁库 `wise-locksmith`,它提供多种锁机制(如文件锁、分布式锁等),有效解决线程安全问题,特别适用于电商平台库存管理等场景。通过 Composer 安装后,开发者可以利用该库确保在高并发下数据的一致性和安全性。
48 6
|
6月前
|
安全 Python
告别低效编程!Python线程与进程并发技术详解,让你的代码飞起来!
【7月更文挑战第9天】Python并发编程提升效率:**理解并发与并行,线程借助`threading`模块处理IO密集型任务,受限于GIL;进程用`multiprocessing`实现并行,绕过GIL限制。示例展示线程和进程创建及同步。选择合适模型,注意线程安全,利用多核,优化性能,实现高效并发编程。
87 3
|
7月前
|
Java
java线程之分支合并框架
java线程之分支合并框架
|
7月前
|
Java 调度
Java并发基础-线程简介(状态、常用方法)
Java并发基础-线程简介(状态、常用方法)
45 0
|
5月前
|
Java Windows
【Azure Developer】Windows中通过pslist命令查看到Java进程和线程信息,但为什么和代码中打印出来的进程号不一致呢?
【Azure Developer】Windows中通过pslist命令查看到Java进程和线程信息,但为什么和代码中打印出来的进程号不一致呢?
|
5月前
|
开发者 C# 存储
WPF开发者必读:资源字典应用秘籍,轻松实现样式与模板共享,让你的WPF应用更上一层楼!
【8月更文挑战第31天】在WPF开发中,资源字典是一种强大的工具,用于共享样式、模板、图像等资源,提高了应用的可维护性和可扩展性。本文介绍了资源字典的基础知识、创建方法及最佳实践,并通过示例展示了如何在项目中有效利用资源字典,实现资源的重用和动态绑定。
154 0
|
5月前
|
Java 开发者
解锁Java并发编程的秘密武器!揭秘AQS,让你的代码从此告别‘锁’事烦恼,多线程同步不再是梦!
【8月更文挑战第25天】AbstractQueuedSynchronizer(AQS)是Java并发包中的核心组件,作为多种同步工具类(如ReentrantLock和CountDownLatch等)的基础。AQS通过维护一个表示同步状态的`state`变量和一个FIFO线程等待队列,提供了一种高效灵活的同步机制。它支持独占式和共享式两种资源访问模式。内部使用CLH锁队列管理等待线程,当线程尝试获取已持有的锁时,会被放入队列并阻塞,直至锁被释放。AQS的巧妙设计极大地丰富了Java并发编程的能力。
55 0
|
6月前
|
设计模式 存储 安全
Java面试题:设计一个线程安全的单例类并解释其内存占用情况?使用Java多线程工具类实现一个高效的线程池,并解释其背后的原理。结合观察者模式与Java并发框架,设计一个可扩展的事件处理系统
Java面试题:设计一个线程安全的单例类并解释其内存占用情况?使用Java多线程工具类实现一个高效的线程池,并解释其背后的原理。结合观察者模式与Java并发框架,设计一个可扩展的事件处理系统
79 1
|
7月前
|
API
linux---线程互斥锁总结及代码实现
linux---线程互斥锁总结及代码实现