《C++多线程编程实战》——1.3 程序结构、执行流和运行时对象

简介:

本节书摘来自异步社区出版社《C++多线程编程实战》一书中的第1章,第1.3节,作者: 【黑山共和国】Milos Ljumovic(米洛斯 留莫维奇),更多章节内容可以访问云栖社区“异步社区”公众号查看。

1.3 程序结构、执行流和运行时对象
编程范式是计算机编程的基本样式,主要有4种范式:命令式、声明式、函数式(或结构式)、面向对象式。C++是当今最流行的面向对象编程语言之一,集功能性、灵活性、实用性于一体。和C一样,程序员能很快地适应它。C++成功的关键在于,程序员可以根据实际需要做相应地调整。

但是,C++学起来并不轻松。有时,你会认为这是一门高深莫测、难以捉摸的语言,一门永远学不完也无法完全理解和掌握的语言。别担心,学习一门语言并不是要掌握它的所有细枝末节,关键要学会如何正确地用语言特性解决特定的问题。实践是最好的老师,根据具体情况尽可能多地使用相应的特性,有助于加深理解。

在给出示例前,我们先介绍一下查尔斯·西蒙尼的匈牙利表示法。他在1977年的博士论文中,使用元编程(Meta-Programming)(一种软件生产方法)在程序设计中制定了标准的表示法。文中规定类型或变量的第1个字母表示数据类型。例如,如果要给一个类命名,Test数据类型应该是CTest。第1个字母C表示Test是一个类。这个方法很不错,因为不熟悉Test数据类型的程序员会马上明白Test是一个类名。基本数据类型也可以这样处理,以int和double为例,iCount表示一个int类型的变量Count,而dValues表示一个double类型的变量Value。有了这些前缀,即使不熟悉代码也很容易识别它们的类型,提高了代码的可读性。

准备就绪
确定安装并运行了Visual Studio(VS)。

操作步骤
根据以下步骤创建我们的第1个程序示例。

1.创建一个默认的C++控制台应用程序[1],命名为TestDemo

2.打开TestDemo.cpp

3.输入下面的代码:

#include "stdafx.h" 
#include <iostream>

using namespace std; 
int _tmain(int argc, _TCHAR* argv[]) 
{ 
  cout << "Hello world" << endl; 
  return 0; 
}```
示例分析
不同的编程技术使得C++程序的结构多种多样。绝大多数程序都必须有``#include`或预处理指令。

`#inlude <iostream>`告诉编译器包含`iostream.`h头文件,该头文件中有许多函数原型。这也意味着函数实现以及相关的库都要放进可执行文件中。因此,如果要使用某个API或函数,就要包含相应的头文件,必要时还必须添加包含API或函数实现的输入库。另外,要注意<header>和"header"的区别。前者`(< >`)表示从解决方案配置的项目路径开始搜索,而后者(`""`)表示从与C++项目相关的当前文件夹开始搜索。

`using``命令指示编译器要使用std名称空间。名称空间中包含对象声明和函数实现,有非常重要的用途。在包含第三方库时,名称空间能最大程度地减少两个不同软件包中同名函数的歧义。

我们需要实现一个程序的入口点:`main`函数。前面提到过, ANSI签名用`main`, Unicode签名用`wmain`,编译器根据项目属性页的预处理器定义确定签名用`_tmain`。对于控制台应用程序,main函数有以下4种不同的原型:

int _tmain(int argc, TCHAR* argv[])
void _tmain(int argc, TCHAR* argv[])
int _tmain(void)
void _tmain(void)`
第1种原型有两个参数:argcargv。第1个参数argc(即,参数计数)表示第2个参数argv(即,参数值)中的参数个数。形参argv是一个字符串数组,其中的每个字符串都代表一个命令行参数。argv中的第1个字符串一定是当前程序的名称。第2种原型和第1种原型的参数类型、参数个数相同,但是返回类型不同。这说明main函数可能返回值,也可能不返回值。该值将被返回给操作系统。第3种原型没有参数,并返回一个整型值。第4种原型既没有参数也没有返回类型。看来,用第1种原型作为练习很不错。

函数体中的第1条语句使用了cout对象。cout是C++中标准输出流的名称。整条语句的意思是:把一系列字符(该例中是Hello world字符序列)插入标准输出流(通常对应的是屏幕)。

cout对象声明在std名称空间的iostream标准文件中。因此,要是用该对象必须包含相应的头文件,并且在_tmain函数前面先声明其所属的名称空间。

在我们使用的原型中(int _tmain(int, _TCHAR*)),_tmain返回一个整数。因此,必须在return关键字后面指定相应的int类型值,本例中是0。向操作系统返回值时,0通常表示执行成功。但是,具体的值由操作系统决定。

这个小程序非常简单。我们以此为例解释main`例程作为每个C++程序入口点的基本结构和用法。

单线程程序按顺序逐行执行。因此,如果把所有的代码都写成一个线程,这样的程序对用户并不友好。

如图1.1所示,应用程序要等用户输入数据后,才能重新获得控制权继续执行。为此,可以创建并发线程来处理用户的输入。这样,应用程序随时都能响应,不会在等待用户输入时毫无反应了。线程处理完自己的任务后,可以给应用程序发信号,告诉程序用户已完成相应操作。


dd480b2f931ea75cec1c57f1bf46eec6324a4166

图1.1 单线程程序按顺序逐行执行

更多讨论
每次我们要在主执行流中单独执行一个操作,都必须考虑使用一个单独的线程。最简单的例子是,实现一边计算一边在进度条上反映计算的进度。想在同一个线程中处理计算和更新进度条,可能行不通。因为如果一个线程既要进行计算又要更新UI,就不能充分地与操作系统绘画交互。因此,一般情况下我们总是把UI线程与其他工作线程分开。

来看下面的例子。假设我们创建了一个用于计算的函数(如,计算指定角度的正弦值或余弦值),我们要同步显示计算过程的进度:

void CalculateSomething(int iCount) 
{ 
  int iCounter = 0; 
  while (iCounter++ < iCount) 
  { 
    // 计算部分 
    // 更新进度条部分 
  } 
}```
由于while循环的每次迭代都忙于依次执行语句,操作系统没有所需的时间逐步更新用户接口(该例中,用户接口指进度条),因此用户见到的可能是空的进度条。待该函数返回后,才会出现已经完全被填满的进度条。出现这种情况的原因是在主线程中创建了进度条。我们应该单独用一个线程来执行CalculateSomething函数,然后在每次迭代中给主线程发信号来逐步更新进度条。前面提到过,线程在CPU中以极快的速度切换,在我们看来进度条的更新与计算进度同步进行。

总而言之,每次处理并行任务时,如果要等待用户输入或依赖外部(如,远程服务器的响应),就应该为类似的操作单独创建一个线程,这样我们的程序才不会挂起无响应。
相关文章
|
26天前
|
存储 编译器 C语言
C++入门: 类和对象笔记总结(上)
C++入门: 类和对象笔记总结(上)
32 0
|
27天前
|
存储 缓存 算法
【C/C++ 性能优化】提高C++程序的缓存命中率以优化性能
【C/C++ 性能优化】提高C++程序的缓存命中率以优化性能
113 0
|
21天前
|
人工智能 机器人 编译器
【C++】Windows端VS code中运行CMake工程(手把手教学)
【C++】Windows端VS code中运行CMake工程(手把手教学)
|
12天前
|
存储 算法 Linux
【实战项目】网络编程:在Linux环境下基于opencv和socket的人脸识别系统--C++实现
【实战项目】网络编程:在Linux环境下基于opencv和socket的人脸识别系统--C++实现
32 6
|
1天前
|
C++
c++的学习之路:7、类和对象(3)
c++的学习之路:7、类和对象(3)
14 0
|
1天前
|
存储 编译器 C语言
c++的学习之路:5、类和对象(1)
c++的学习之路:5、类和对象(1)
10 0
|
6天前
|
编译器 C++
自从学了C++之后,小雅兰就有对象了!!!(类与对象)(中)——“C++”
自从学了C++之后,小雅兰就有对象了!!!(类与对象)(中)——“C++”
|
6天前
|
存储 编译器 C++
自从学了C++之后,小雅兰就有对象了!!!(类与对象)(上)——“C++”
自从学了C++之后,小雅兰就有对象了!!!(类与对象)(上)——“C++”
|
7天前
|
C++
【C++成长记】C++入门 | 类和对象(下) |Static成员、 友元
【C++成长记】C++入门 | 类和对象(下) |Static成员、 友元
|
7天前
|
存储 编译器 C++
【C++成长记】C++入门 | 类和对象(中) |拷贝构造函数、赋值运算符重载、const成员函数、 取地址及const取地址操作符重载
【C++成长记】C++入门 | 类和对象(中) |拷贝构造函数、赋值运算符重载、const成员函数、 取地址及const取地址操作符重载