65.9K
CodeProject 正在变化。 阅读更多。
Home

线程入门,熟悉概念和API

starIcon
emptyStarIcon
starIcon
emptyStarIconemptyStarIconemptyStarIcon

1.26/5 (17投票s)

2005年9月7日

3分钟阅读

viewsIcon

43434

线程入门,概念和API

介绍:线程入门。
大家好,我们中的许多人都是伟大的软件工程师,无论如何

我注意到一个事实,我们中的许多人并不清楚

操作系统给我们的伟大礼物。是的

没错,这个礼物叫做多线程环境,

对一些人来说,这些词是噩梦,而对

另一些人来说,这个问题并没有说明太多,我们中的许多人

从未听说过多线程。

本文主要面向初学者和那些不

了解多线程概念的人。


第一部分:什么是线程,
线程是以下内容的集合
{程序计数器、堆栈、CPU},我们

习惯的普通程序只有一个线程,即主线程。我们要做的

是向我们的程序添加一个线程

.

我们将看到创建简单线程的代码示例 

,

这是将作为线程运行的函数



void Thread1(int * a_iNum)
{
 while(*a_iNum>0)
 {
  cout<<*a_iNum<<" ";
  (*a_iNum)--;
 }
}

这是将创建线程的函数



void RunThread1()
{
 int l_iNum=10;
 HANDLE

l_htRun=::CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)Threa

d1,&l_iNum,0,0);

 if(l_htRun==NULL)
 {
  cout<<"cout could not run the thread \n";
  return;
 }

 WaitForSingleObject(l_htRun,INFINITE);

 cout<<"The  thread was finished  \n";
}

 


正如我们所见 ,此线程没有执行任何特殊操作

,目标是学习创建线程的API,

我想现在解释一些事情,

LPTHREAD_START_ROUTINE - 这是函数的 typedef,
在 Windows 头文件中,它看起来像这样,
typedef int (*LPTHREAD_START_ROUTINE)(void*);

(LPTHREAD_START_ROUTINE)Thread1 - 这意味着我们

正在对函数类型进行强制转换,以便它

兼容并且代码将被正确编译。

第四个参数是指向我们

要传递给线程的参数的指针。

我现在不会讨论其他参数,我们

稍后会讨论它们。

我们可以看到 CreateThread 函数返回类型

HANDLE,我们对这个 HANDLE 所做的是

等待直到线程完成。

因此,到目前为止,我们了解到创建线程非常容易

我们还没有学到任何有趣的东西

但对于初学者来说,这没关系。

 

我将要展示的示例对于

线程概念的理解非常重要。



void Thread2(int * a_pNum)
{
 while(*a_pNum)
 {
  cout<<"A";
  cout.flush();
  ::Sleep(100);
 }

}

void Thread3(int * a_pNum)
{
 while(*a_pNum)
 {
  cout<<"B";
  cout.flush();
  ::Sleep(100);
 }
}

 


void RunThread2AndThread3()
{
 int a_iNum2=1;
 int a_iNum3=1;
 
 HANDLE l_hArr[2];
 l_hArr[0]

=::CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)Thread2,&a_i

Num2,0,0);
 l_hArr[1]

=::CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)Thread3,&a_i

Num3,0,0);


 WaitForMultipleObjects(2,l_hArr,true,INFINITE);

}


我们可以在此示例中看到的是 2 个正在

运行的线程,其中一个打印 A,第二个打印 B 到

屏幕。
当我在讲授线程时,我的一个学生

问我“这段代码怎么样?”



void SeeTheProblem()
{
 int a_iNum2=1;
 int a_iNum3=1;

 Thread2(&a_iNum2);
 a_iNum2=0;
 Thread3(&a_iNum3);
 a_iNum3=0;
 
}


它会做同样的工作,不是吗?

正如我们所见,此函数将无法完成工作

我们可以看到线程在它进入函数名称 Thread2 后会陷入停滞

并且它不会

传递此行,因为此函数是无限的。
但是,当我们以线程的形式运行该函数时,它将运行

因为该线程运行在不同的 CPU 上。


注释
1.我添加的 sleep 函数并不那么重要,

如果我没有添加它,结果是你的电脑的 CPU

将是 100%。
2.cout 的 .flush() 是为了将数据刷新到

屏幕,众所周知,cout 不会立即打印

到屏幕,只有当它的缓冲区已满时,所以我

必须刷新日期才能显示 ,这只是为了

这个例子,尽量不要在运行时代码中使用 flush 函数

因为它不是很有效 。

 


第三部分,使用全局变量,

众所周知,来自一个进程的所有线程都可以访问

进程的数据段,或者换句话说,它们可以

访问全局变量,这是线程的优势之一,也是

问题之一(对于程序员)。

 



int g_iGoOn=1;

void Thread4(int * a_iNum)
{
 while(g_iGoOn)
 {
  cout<<*a_iNum<<"\n";
  ::Sleep(5);
 }

}

 

 


void RunThread4()
{
 HANDLE l_hArr[2];
 int a_iNum1=10;
 int a_iNum2=11;
 l_hArr[0]

=::CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)Thread4,&a_i

Num1,0,0);
 l_hArr[1]

=::CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)Thread5,&a_i

Num2,0,0);


 ::Sleep(5000);
 g_iGoOn=0;

 WaitForMultipleObjects(2,l_hArr,true,INFINITE);


}


在这段代码中,我们可以看到线程如何使用

全局变量,以便检查是否继续或

停止他们的活动。


这些例子非常简单,目标是向

用户展示编写代码有多简单,

查看本文的第二部分,以了解

同步问题和解决方案,

在下一篇文章中,我们将更多地讨论这些函数

WaitForSingleObject、WaitForMultipleObject、互斥锁、事件、信号量。

 

© . All rights reserved.