1、理解线程

  要讲解线程,不得不说一下进程,进程是应用程序的执行实例,每个进程是由私有的虚拟地址空间、代码、数据和其它系统资源组成。进程在运行时创建的资源随着进程的终止而死亡。线程的基本思想很简单,它是一个独立的执行流,是进程内部的一个独立的执行单元,相当于一个子程序,它对应于Visual C++中的CwinThread类对象。单独一个执行程序运行时,缺省地包含的一个主线程,主线程以函数地址的形式出现,提供程序的启动点,如main()或WinMain()函数等。当主线程终止时,进程也随之终止。根据实际需要,应用程序可以分解成许多独立执行的线程,每个线程并行的运行在同一进程中。

  一个进程中的所有线程都在该进程的虚拟地址空间中,使用该进程的全局变量和系统资源。操作系统给每个线程分配不同的CPU时间片,在某一个时刻,CPU只执行一个时间片内的线程,多个时间片中的相应线程在CPU内轮流执行,由于每个时间片时间很短,所以对用户来说,仿佛各个线程在计算机中是并行处理的。操作系统是根据线程的优先级来安排CPU的时间,优先级高的线程优先运行,优先级低的线程则继续等待。

    线程被分为两种:用户界面线程和工作线程(又称为后台线程)。用户界面线程通常用来处理用户的输入并响应各种事件和消息,其实,应用程序的主执行线程CWinAPP对象就是一个用户界面线程,当应用程序启动时自动创建和启动,同样它的终止也意味着该程序的结束,进程终止。工作线程用来执行程序的后台处理任务,比如计算、调度、对串口的读写操作等,它和用户界面线程的区别是它不用从CWinThread类派生来创建,对它来说最重要的是如何实现工作线程任务的运行控制函数。工作线程和用户界面线程启动时要调用同一个函数的不同版本;最后需要读者明白的是,一个进程中的所有线程共享它们父进程的变量,但同时每个线程可以拥有自己的变量。

参考:http://www.cnblogs.com/cy163/archive/2006/11/02/547428.html

2、相关函数

_beginthread和_endthread函数

该函数是C Runtime Library中的函数。其原型如下
unsigned long _beginthread(

void( __cdecl *start_address )( void * ),//线程函数的起始地址

unsigned stack_size,//堆栈大小,设置0为系统默认值

void *arglist );//传递给线程函数的参数,没有则为NULL

“该函数被认为是头脑简单的函数”,使用该函数导致无法有效的控制被创建线程,如不能在启动时将该线程挂起,无法为该线程设置优先权等。另外,无法利用这个Handle来等待该线程结束等操作。该函数是早期的C Runtime Library的产物,不提倡使用,后期的改良版本为_beginthreadex。
通过_beginthread启动的线程在应当通过调用_endthread结束,以保证清除与线程相关的资源。_endthread的原型为:void _endthread(void);

参考:http://blog.csdn.net/cbnotes/article/details/8331632

WaitForSingleObject函数

DWORD WINAPI WaitForSingleObject(

__in HANDLE hHandle, //内核对象

__in DWORD dwMilliseconds);//需要等待的时间(毫秒)

理解1:

WaitForSingleObject函数用来检测hHandle事件的信号状态,在某一线程中调用该函数时,线程暂时挂起,如果在挂起的dwMilliseconds毫秒内,线程所等待的对象变为有信号状态,则该函数立即返回;如果超时时间已经到达dwMilliseconds毫秒,但hHandle所指向的对象还没有变成有信号状态,函数照样返回。参数dwMilliseconds有两个具有特殊意义的值:0和INFINITE。若为0,则该函数立即返回;若为INFINITE,则线程一直被挂起,直到hHandle所指向的对象变为有信号状态时为止。

理解2:
WaitForSingleObject函数需要传递一个内核对象句柄,该句柄标识一个内核对象,如果该内核对象处于未通知状态,则该函数导致线程进入阻塞状态;如果该内核对象处于已通知状态,则该函数立即返回WAIT_OBJECT_0。第二个参数指明了需要等待的时间(毫秒),可以传递INFINITE指明要无限期等待下去,如果第二个参数为0,那么函数就测试同步对象的状态并立即返回。如果等待超时,该函数返回WAIT_TIMEOUT。如果该函数失败,返回WAIT_FAILED。

参考:http://baike.baidu.com/item/WaitForSingleObject   http://blog.csdn.net/jueqing007/article/details/6901568

3、代码示例

1). 使用线程

#include <process.h>
#include <iostream>void ThreadMain(void *para)
{int *p = (int*)para;std::cout << *p << std::endl;_endthread();
}int main()
{int i = 1234;_beginthread(ThreadMain, 0, &i); //(线程函数的起始地址,堆栈大小,传递给线程函数的参数)getchar();return 0;
}

结果:

2). 单线程测试

#include <process.h>
#include <iostream>
#include <Windows.h>static char *arr = NULL;
int main()
{int size = 1000 * 1000 * 1000;arr = new char[size];long long start = GetTickCount(); //开始计时for (int i = 0; i < size; i++)    //简单测试 做赋值操纵{arr[i] = i;}long long end = GetTickCount(); //计时结束std::cout << "main deal ms = " << end - start << std::endl;getchar();return 0;
}

结果:

3). 多线程测试

main.cpp
//#include <process.h>
#include <iostream>
#include <Windows.h>
#include "XThread.h"static char *arr = NULL;                   //测试用,实际项目中不建议用static数组class MyThread :public XThread
{
public:int b = 0;                         //数组beginint e = 0;                         //数组endvoid Main(){for (int i = b; i <= e; i++){arr[i] = i;}}
};
int main()
{int size = 1000 * 1000 * 1000;arr = new char[size];int tsize = 4;                     //线程数(参考计算机核数如4核,将每个线程分配一个核)MyThread *ths = new MyThread[tsize];long long start = GetTickCount();  //开始计时 15-29ms精度for (int i = 0; i < tsize; i++)    //数据平均分配给tsize个线程,逐线程start{ths[i].b = i*(size / tsize);ths[i].e = ths[i].b + (size / tsize);if (ths[i].e >= size)      //最后一个数时{ths[i].e = size - 1;}ths[i].Start();}for (int i = 0; i < tsize; i++)    //逐个等待线程结束{ths[i].Wait();}long long end = GetTickCount();    //计时结束std::cout << "main deal ms = " << end - start << std::endl;getchar();return 0;
}

XThread.h

#pragma once
class XThread
{
public:bool Start();void Wait();virtual void Main() = 0; //纯虚函数XThread();virtual ~XThread();
private:unsigned int th = 0;    //存线程句柄
};

XThread.cpp

#include "XThread.h"
#include <process.h>
#include <Windows.h> //Windows.h尽量用在.cpp文件中,用在.c文件中常出现次序问题static void ThreadMain(void *para)
{XThread *th = (XThread *)para;th->Main();_endthread();
}bool XThread::Start()
{th = _beginthread(ThreadMain, 0, this);//句柄th为unsigned ,显示强转成intif ((int)th <= 0){return false;}return true;
}void XThread::Wait()    //等待线程结束
{WaitForSingleObject((HANDLE)th, INFINITE);//(内核对象句柄,需要等待的时间ms)
}XThread::XThread()
{
}XThread::~XThread()
{
}

结果:

单线程(Thread)与多线程的比较:

上述结果显示4线程比单线程节省一半的时间,当然这对于不同的计算机配置来说,结果不尽相同,处理速度都会有所提高。

对于CPU来说,随着主频(cpu内核工作时钟频率,表示在CPU内数字脉冲信号震荡的速度,等于外频(系统基本时间)乘倍频)的不断攀升,X86构架的硬件逐渐成为瓶颈,最高为4G,事实上目前3.6G主频的CPU已经接近顶峰。

多线程编程的目的,就是"最大限度地利用CPU资源",当某一线程的处理不需要占用CPU而只和I/O,OEMBIOS等资源打交道时,让需要占用CPU资源的其它线程有机会获得CPU资源。每个程序执行时都会产生一个进程,而每一个进程至少要有一个主线程。这个线程其实是进程执行的一条线索,除了主线程外你还可以给进程增加其它的线程,也即增加其它的执行线索,由此在某种程度上可以看成是给一个应用程序增加了多任务功能。当程序运行后,您可以根据各种条件挂起或运行这些线程,尤其在多CPU的环境中,这些线程是并发运行的。多线程就是在一个进程内有多个线程。从而使一个应用程序有了多任务的功能。多进程技术也可以实现这一点,但是创建进程的高消耗(每个进程都有独立的数据和代码空间),进程之间通信的不方便(消息机制),进程切换的时间太长,这些导致了多线程的提出,对于单CPU来说(没有开启超线程),在同一时间只能执行一个线程,所以如果想实现多任务,那么就只能每个进程或线程获得一个时间片,在某个时间片内,只能一个线程执行,然后按照某种策略换其他线程执行。由于时间片很短,这样给用户的感觉是同时有好多线程在执行。但是线程切换是有代价的,因此如果采用多进程,那么就需要将线程所隶属的该进程所需要的内存进行切换,这时间代价是很多的。而线程切换代价就很少,线程是可以共享内存的。所以采用多线程在切换上花费的比多进程少得多。但是,线程切换还是需要时间消耗的,所以采用一个拥有两个线程的进程执行所需要的时间比一个线程的进程执行两次所需要的时间要多一些。即采用多线程不会提高程序的执行速度,反而会降低速度,但是对于用户来说,可以减少用户的响应时间。上述结果只是针对单CPU,如果对于多CPU或者CPU采用超线程技术的话,采用多线程技术还是会提高程序的执行速度的。因为单线程只会映射到一个CPU上,而多线程会映射到多个CPU上,超线程技术本质是多线程硬件化,所以也会加快程序的执行速度。

参考:http://blog.csdn.net/douglax/article/details/1532258

理解线程/多线程处理数组(MultiThreaded dealing with arrays)相关推荐

  1. 线程运行程序c语言,理解线程1 C语言示例的程序

    一个简单的C语言实现的线程示例 在看<Beginning Linux Programming>时,为了更好的理解线程的概念,书中列举了这样一个小例子: #include #include ...

  2. 通过ThreadPoolExecutor与ForkJoinPool比较,分别对比其execute ,submit 等方法提交线程池任务的区别,来深入理解线程池及并发编程

    前言 以前使用线程池,对execute . submit 等方法提交线程池任务的区别比较模糊,现在通过ThreadPoolExecutor与ForkJoinPool比较,分别对比其execute ,s ...

  3. 深入理解JavaScript类数组

    起因 写这篇博客的起因,是我在知乎上回答一个问题时,说自己在学前端时把<JavaScript高级程序设计>看了好几遍. 于是在评论区中,出现了如下的对话: 天啦噜,这话说的,宝宝感觉到的, ...

  4. 【C 语言】数组 ( 多维数组本质 | 步长角度 理解 多维数组本质 )

    文章目录 一.从 步长角度 理解 多维数组本质 二.代码示例 一.从 步长角度 理解 多维数组本质 声明一个二维数组 ; // 声明一个多维数组int array[2][3]; 二级指针 : arra ...

  5. 聊聊JVM(五)从JVM角度理解线程

    这篇说说如何从JVM的角度来理解线程,可以对Java的线程模型有一个更加深入的理解,对GC的一些细节也会理解地更加深刻.本文基于HotSpot的OpenJDK7实现. 我们知道JVM主要是用C++实现 ...

  6. 一个DEMO让你彻底理解线程池

    目录 一.简介 二.线程池任务场景 场景一:提交5个任务,执行总耗时500ms 场景二:提交10个任务,执行总耗时500ms 场景三:提交11个任务,执行总耗时1000ms 场景四:提交20个任务,执 ...

  7. Java核心(二)深入理解线程池ThreadPool

    本文你将获得以下信息: 线程池源码解读 线程池执行流程分析 带返回值的线程池实现 延迟线程池实现 为了方便读者理解,本文会由浅入深,先从线程池的使用开始再延伸到源码解读和源码分析等高级内容,读者可根据 ...

  8. linux c99 可变长数组,C中不支持可变长度数组C99(Variable length arrays C99 not supported in C)...

    C中不支持可变长度数组C99(Variable length arrays C99 not supported in C) 在Visual Studio 2005中,我正在尝试编译.c文件: int ...

  9. 集合 数组 定义 转换 遍历 Arrays API MD

    Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...

  10. [转]从根上理解高性能、高并发:深入计算机底层,理解线程与线程池

    系列 <从根上理解高性能.高并发(一):深入计算机底层,理解线程与线程池> <从根上理解高性能.高并发(二):深入操作系统,理解I/O与零拷贝技术> <从根上理解高性能. ...

最新文章

  1. 2019年智能手机AI要被深度开发,这五项技术将是重点
  2. 美国防部官员讨论量子科学、5G和定向能的发展
  3. JNI设置C++与java的结合(2)
  4. 企业员工工资管理系统
  5. python一些简单操作_python列表的基本操作有哪些
  6. router vue 页签文字_vue-router实现tab标签页(单页面)详解
  7. Jira Bamboo中创建Windows Remote Agent的注意事项
  8. ubuntu 16.04 搭建无线共享热点(PC 无线直连Android移动终端 调试,监控屏幕)
  9. js中(function(){…})()立即执行函数写法理解
  10. sql 2005性能调优
  11. Javascript框架设计思路图
  12. OpenCV(0)---机器学习库
  13. 3D图形原理和一些基本概念
  14. 解压.tar.bz2文件出现错误
  15. FreeImage使用方法
  16. 压力传感器的误差补偿
  17. 抖音自拍特效如何java实现_抖音特效在 Web 端的实现
  18. 痛苦的刷路由器 破校园网 小米mini潘多拉
  19. 计算机课开机心得学生,计算机课学习心得范文体会范文(精选8篇)
  20. 22-05-21 西安 javaweb(07) HttpServletRequest和HttpServletResponse、转发与重定向、web应用的路径问题、解决中文乱码问题

热门文章

  1. [2019杭电多校第八场][hdu6667]Roundgod and Milk Tea
  2. jeecg导出Excel
  3. Luogu P5008 逛庭院
  4. 存储过程,是否有查看所有项目功能
  5. Visro 应用的前端模板工具介绍 -JsRender
  6. FFT海水模拟(又来了-_-b)
  7. MyEclipse或Eclipse中工程的导入和导出
  8. RabbitMQ小笔记
  9. JAVAWeb项目 微型商城项目-------(七)后台添加用户管理和商品类型管理操作
  10. python 整合同类数据求分位值_【利用python进行数据分析】数据聚合与分组运算...