理解线程/多线程处理数组(MultiThreaded dealing with arrays)

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;                         //数组begin
	int e = 0;                         //数组end
	void 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 ,显示强转成int
	if ((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









  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

惊鸿一博

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值