今天刚看了关于多线程方面的东东,趁热打铁总结下。
进程与线程
在说线程前有必要先说下进程,一个应用程序执行后就可以按成一个进程了。当然对于一个应用程序而言,里面肯定有许多任务要执行。学过单片机的人应该都知道,假如我们要实现闹钟并用按键调节时间,那么这里面就包含了如下任务:显示时间(假设是数码管),按键处理,闹铃提示。在这里的每个任务我们可以看成一个线程,说白了线程就是一个能实现应用程序某一功能的一个子函数。那么为什么会有多线程的出现呢?
多线程
多线程,字面理解就是多个任务同时进行处理。但在实际中,我们的应用程序可能只是在一个处理器上工作。那么这就有一个问题出现了,既然只有一个处理器,如何能同时做到显示时间的时时更新任务以及按键处理的任务呢?其实多任务本质上应用了人的视觉暂留效应,操作系统给每个线程(即是任务)分配一个很短的时间片,我们假设这段时间长为T。线程1执行T时间的程序后就接着执行线程2里的程序,然后是线程3,4,5等等。实际上T是很短的时间,所以给人的感觉就像是许多任务同时进行。在实际生活中,我们自身身上就有“多线程”的例子:我们把使用左手作画和右手作画看成两个线程,在这种情况下作画,你的大脑肯定是在很短的一段时间内把注意力放在左手作画上然后过了这个很短时间后你又迅速把注意力放在右手上,如此反复,给人的感觉像是你能同时用两个手作画。
多线程出现的必要性?
假如现在有个铁路售票系统,在这个系统中有很多个售票窗口。在售票的过程中会出现下面这种情况:售票窗口1因为某种原因不能顺利完成售票交易,如果按照传统的程序解决方式,先判断1号窗口是否完成交易,完成就判断2号窗口,未完成就等待1号窗口完成。我想无论是售票者还是购票者迟早有一天会疯的,但用多线程就能解决掉这个问题。我们把每个窗口的售票交易看成一个线程,假设线程1不能在规定的时间T内完成交易,那么此时售票窗口1就不能NB哄哄的一个人独占着处理器了,T时间后线程2依然能顺利地进行售票交易,不用再看窗口1的脸色了。
还有一种常见的情况是:假如我们现在处理按键处理这一任务的时候,里面牵涉到消抖的过程,传统的方法是用delay函数,只要delay没执行结束,按键处理这一任务就独占CPU,其他任务比如闹钟提示就不能得到及时的响应。如若引入多线程这一问题就得意解决。所以,多线程的出现标志着“任(务)任(务)平等”的社会主义到来。
当然,多线程的出现偶尔还能让我们装下B。比如上文提到的左右手同时作画,实际我们做不到,但是只要装B装的让人眼花缭乱,让人眼分不清是非,那么就等待着众人的赞扬声吧,甚至女人的投怀送报。
由于我看的是windows下的多线程,所以下面的东东不会让广大电子工程师产生兴趣的,不过我认为无论是什么操作系统,其多线程的核心思想是不变的,所以,我现在对windows的多线程进行总结就是变向的对ucos或者其他系统的多线程方面知识点的总结。
一.一个得不到执行的线程
#include<windows.h>
#include<iostream>
using namespace std;
DWORD WINAPI Fun1(
LPVOID lpParameter
// thread data
);
//线程1的函数声明
void main()
{
HANDLE hThread1;
//定义一个线程的句柄
hThread1 = CreateThread(NULL,0,Fun1,NULL,0,NULL);
//创建线程1的句柄
CloseHandle(hThread1);
//释放线程1的句柄
cout<<"main Thread is running"<<endl;
//主线程的任务
}
DWORD WINAPI Fun1(
LPVOID lpParameter
// thread data
)
{
cout<<"Threa1 is running";
//线程1的任务
return
0;
}
这段程序建立了两个线程:一个是main函数,另外一个就是Func1函数。这两个线程里执行的任务就是简单的输出相应的字符串。
建立新的线程的API函数是CreateThread,它的语法如下:
hThread1 = CreateThread (&security_attributes, dwStackSize, ThreadProc,
pParam, dwFlags, &idThread) ;
第一个参数是指向SECURITY_ATTRIBUTES型态的结构的指针。在Windows 98中忽略该参数。在Windows NT中,它被设为NULL。第二个参数是用于新线程的初始堆栈大小,默认值为0。在任何情况下,Windows根据需要动态延长堆栈的大小。
CreateThread的第三个参数是指向线程函数的指标。函数名称没有限制,但是必须以下列形式声明:
DWORD WINAPI ThreadProc (PVOID pParam) ;
CreateThread的第四个参数为传递给ThreadProc的参数。这样主线程和从属线程就可以共享数据。
CreateThread的第五个参数通常为0,但当建立的线程不马上执行时为旗标CREATE_SUSPENDED。线程将暂停直到呼叫ResumeThread来恢复线程的执行为止。第六个参数是一个指标,指向接受执行绪ID值的变量。
CloseHandle(hThread1); 这个函数的作用我不是太清楚,网上说是让线程的计数器清0,关于计数器后面也会提到。
还有一点要注意的是,在进行进程函数声明时,统一按照如下声明:
DWORD WINAPI Fun1(
LPVOID lpParameter
// thread data
);
在上述声明中唯一能改变的就是线程的函数名Func1.
这个程序运行后,屏幕上只输出main Thread is running,说明线程1未被执行。首先说明的是,当主线程结束后就标志着进程的结束。当主线程执行完向屏幕输出字符串的任务后,线程1还没来得及执行,主线程就立即返回了,由于主线程main是void类型的,所以未出现return的字样。要想让线程1得到执行,只要让主线程返回之前添加一延时函数Sleep,这里的延时函数不同于我们上文提到的delay,sleep不会独占cpu的,只是表示主线程在该段时间内放弃执行权,之后就能出现让线程1的任务“飞一会”的效果了。
void main()
{
HANDLE hThread1;
//定义一个线程的句柄
hThread1 = CreateThread(NULL,0,Fun1,NULL,0,NULL);
//创建线程1的句柄
CloseHandle(hThread1);
//释放线程1的句柄
cout<<"main Thread is running"<<endl;
//主线程的任务
Sleep(100); //主线程在这100ms内放弃执行权
}
写这些的目的纯粹的个人的一种总结,由于刚接触VC没有多久,所以很多总结可能流于表面。当然,我的总结中可能会有一些疑问,希望大虾帮忙解决下。比如上面的程序,显示的字符串是有问题的,主线程和线程1之间的字符串显示是交织在一起的,并非是隔行显示。但是把using namespace std这句删掉,并把#include<iostream>改成#include<iostream.h>这一问题就得到解决了。
下面将说下“同步线程的引出”。
|