创建一个线程,自然有一个对应的系统API来完毕。CreateThread这个函数就用来创建线程的。

各种參数的用途我就不多说了,这里直接贴一个我自己练习的样例

1、以下是一个创建一个线程的样例,当然,不过创建;

#include<windows.h>
#include<iostream>
using namespace std; DWORD WINAPI Fun1Pro(LPVOID laparameter);
int main()
{
HANDLE hthread1;
hthread1 = CreateThread(NULL, 0, Fun1Pro, NULL, 0, NULL);
CloseHandle(hthread1);
cout << "main thread is running \n";
return 0;
} DWORD WINAPI Fun1Pro(LPVOID Laparameter)
{
printf("The New Thread is running\n");
return 0;
}

在C++中,对于对象的引用,也是通过引用计数的方式来处理的,所以上述代码中的closehandle这个函数,仅仅是在主线程中关闭了线程的一个句柄。可是实际上创建的这个新线程还是存在的。仅仅只是这个新线程的内核对象中对这个线程的引用计数-1.否则假设不运行这种一个函数,即使这个线程运行完了。也会由于主线程中仍然有引用这个线程的痕迹从而这个线程的引用计数不为0,则不能彻底删除,仅仅有当整个进程计数的时候。才会进行清理。

执行这个程序之后能够发现,并没有看到新线程输出的那句话。

原因是这种。进程启动之后先运行了主线程,由于操作系统是通过分配给线程时间片的方式来让线程运行的,所以。在主线程的时间片内。没有特殊情况是没人打断他的,一直在运行,到了创建新的子线程的时候。由于主线程的时间片没到。所以,不可以运行子线程,之后立即又关闭了子线程的句柄,主线程运行完了,就说明整个进程也就结束了,回收了全部的资源,子线程基本上是无用的。假设要想看见子线程运行的话。那么就须要让主线程暂停运行,这时候。操作系统中的分派器就会在队列里面找到一个新的线程来运行。

让一个线程停止执行的的办法就是利用sleep()函数。使其可以暂停一下。

能够在main函数中return 0;这条语句的前面加上sleep他就 会在主线程结束之前暂停,从而运行新的子线程。

那么主线程和子线程他们直接的执行顺序究竟是什么样的呢?因为线程和进程的执行时间都是又操作系统的时间片来决定的,我对代码做了例如以下改动:

#include<windows.h>
#include<iostream>
using namespace std; DWORD WINAPI Fun1Pro(LPVOID laparameter);
int index = 0;
int main()
{
index = 0;
HANDLE hthread1;
hthread1 = CreateThread(NULL, 0, Fun1Pro, NULL, 0, NULL); CloseHandle(hthread1);
while(index++<100)
cout << "main thread is running \n";
//Sleep(10);
return 0;
} DWORD WINAPI Fun1Pro(LPVOID Laparameter)
{
while(index++<100)
printf("The New Thread is running\n");
return 0;
}

执行结果如图:

从图中我们就能够印证上面所说的了,线程的执行时间是靠时间片来决定的,当主线程的时间片执行完,可是整个主线程还没执行完的时候,操作系统的分派器也会将主线程增加线程的就绪队列。从队列中找出新的子进程来执行。

2、利用相互排斥对象实现同步:

相互排斥对象属于内核对象,他可以保证线程对单个资源拥有相互排斥的訪问权,也就是说,仅仅能由一个线程在同一时间訪问该资源。一个相互排斥对象包含:使用数量,线程ID,计数器。

ID表明如今是哪个线程拥有这个相互排斥对象。计数器用于指明该线程拥有相互排斥对象的次数。

须要调用CreateMutex函数来创建相互排斥对象。

通过WaitForSingleObject函数来请求相互排斥对象,通过ReleaseMutex函数来释放相互排斥对象。

可能看到这里大家还是不能明确为什么相互排斥对象能够实现进程间的同步作用。

首先。我们要创建一个相互排斥对象,此时这个相互排斥对象处于一个有信号状态,当我们用wait函数来请求一个相互排斥对象的时候, 那么我们此时的这个线程是可以获得相互排斥对象的訪问权,从而開始运行wait函数后面的共享代码区,同一时候,当调用一次wait的时候,相互排斥对象就从有信号变为无信号状态,由于相互排斥对象就是保证对单个资源的相互排斥訪问,假设此时还有其它的新的线程来请求相互排斥对象的訪问权从而运行共享代码的话。那么此时他得到的相互排斥对象是无信号状态,是不可以继续运行的,此时的线程会处于等待状态。当第一个线程运行完共享代码区的时候。我们要释放如今所使用的这个相互排斥对象,即用releasemutex来进行释放。他会将相互排斥对象从无信号状态设置为有信号状态,此时依据请求的先后顺序,之前在wait函数那里等待相互排斥对象信号的两个线程中的一个,就会被同意获得相互排斥的对象的訪问权,从而运行共享存储区的代码。

事实上上面的相互排斥对象和操作系统中的PV操作是同理的。

共享代码就是在wait函数和release函数中间的代码。每次仅仅能同意一个线程获得相互排斥对象的訪问权从而进入到临界区里面。wait是请求操作,release是恢复操作。

相互排斥对象另一个重要的机制,就是他包含的线程的ID.假设是线程1请求了一个相互排斥对象。那么假设线程二想用这个相互排斥对象之前,必须由线程1进行释放,在释放的过程中,相互排斥对象会拿出他维护的线程ID和释放它的线程进行比較,仅仅有两者同样的情况下才可以释放这个相互排斥对象。否则假设线程ID和释放者不一样 ,则不可以释放相互排斥对象。

CreateMutex函数中的第二个參数有两个取值,一个是false,一个是true.false代表当前的线程不拥有这个相互排斥对象,相互排斥对象处于有信号状态。true呢表示当前的线程拥有这个相互排斥对象,即当前的线程和该相互排斥对象绑定,相互排斥对象处于无信号状态。相当于默认的运行了wait函数。假设此时在这个线程里面再调用wait函数,尽管此时的相互排斥对象是无信号状态,可是由于调用他的线程和相互排斥对象内部维护的线程ID是相等的,所以依旧会获得该相互排斥对象的控制权。可是此时有一个很重要的一点,就是这个相互排斥对象的计数器+1了。一開始就说过。相互排斥对象的计数器代表了拥有这个相互排斥对象的线程拥有它的次数,所以每当调用一次wait函数的时候。计数器都会+1.所以。假设说当前线程不须要这个相互排斥对象的时候,须要调用两次释放函数,计数器才会减为0,这也就告诉了我们。释放函数的功能实际上就是让计数器-1.

3、关于相互排斥对象另一个非常重要的功能:

当一个线程里面利用wait函数请求相互排斥对象运行完成之后,在线程中并没有调用release函数来释放这个对象。所以在线程结束之后操作系统假设发现线程已经终止的话。他会自己主动帮我们释放掉这个相互排斥对象。把它变为有信号状态。

4、怎样保证仅仅有一个实例执行:

我们都知道程序仅仅是代码,一个程序执行起来能够有多个实例,那么假设仅仅让这个程序仅仅有一个实例在执行呢。

解决的方法就是利用命名的相互排斥对象来实现。原因就是。假设在调用创建相互排斥对象的函数的时候。假设之前已经有该命名的相互排斥对象存在,那么就返回已经创建的相互排斥对象,不再创建 新的,这时getlasterror将返回error_alreday_exists。

所以,假设createmutex返回的是一个有效句柄,接下来就要推断getlasterror的返回值是什么,从而断定我们 是不是在先前已经创建过了一个相互排斥对象,也就相当于已经有一个实例正在执行。

VC++学习之多线程(2)的更多相关文章

  1. 孙鑫VC学习笔记:多线程编程

    孙鑫VC学习笔记:多线程编程 SkySeraph Dec 11st 2010  HQU Email:zgzhaobo@gmail.com    QQ:452728574 Latest Modified ...

  2. VC++学习之VC中常见问题

    VC++学习之VC中常见问题 (1)为什么某个类突然在工作区间里面突然看不见了? 只是类隐藏了,打开FILEVIEW,找到隐藏类的头文件,随便敲一下键盘的空格键,类就会在CLASSVIEW中显示了 ( ...

  3. VC++学习之进程和线程的区别

    VC++学习之进程和线程的区别 一.进程        进程是表示资源分配的基本单位,又是调度运行的基本单位.例如,用户运行自己的程序,系统就创建一个进程,并为它分配资源,包括各种表格.内存空间.磁盘 ...

  4. 0037 Java学习笔记-多线程-同步代码块、同步方法、同步锁

    什么是同步 在上一篇0036 Java学习笔记-多线程-创建线程的三种方式示例代码中,实现Runnable创建多条线程,输出中的结果中会有错误,比如一张票卖了两次,有的票没卖的情况,因为线程对象被多条 ...

  5. Java学习笔记-多线程-创建线程的方式

    创建线程 创建线程的方式: 继承java.lang.Thread 实现java.lang.Runnable接口 所有的线程对象都是Thead及其子类的实例 每个线程完成一定的任务,其实就是一段顺序执行 ...

  6. C#多线程学习(一) 多线程的相关概念(转)

    什么是进程?当一个程序开始运行时,它就是一个进程,进程包括运行中的程序和程序所使用到的内存和系统资源.而一个进程又是由多个线程所组成的. 什么是线程?线程是程序中的一个执行流,每个线程都有自己的专有寄 ...

  7. VC++ 学习笔记(序):神一样的语言

    总的来说,我觉得VC++是一门神一样的语言——它是公认最强大.最复杂的:它一切以效率为第一要务,却又不肯落伍,拼命兼容现在的新的语言设计特点.本来在别的语言很容与就避开的问题,在这里要用很高的技巧去设 ...

  8. C#多线程学习(一) 多线程的相关概念

    什么是进程?当一个程序开始运行时,它就是一个进程,进程包括运行中的程序和程序所使用到的内存和系统资源.而一个进程又是由多个线程所组成的. 什么是线程?线程是程序中的一个执行流,每个线程都有自己的专有寄 ...

  9. [转载]C#多线程学习(一) 多线程的相关概念

    原文地址:http://www.cnblogs.com/xugang/archive/2008/04/06/1138856.html 什么是进程?当一个程序开始运行时,它就是一个进程,进程包括运行中的 ...

随机推荐

  1. 从Date类型字段获得当日周几的DAYNAME函数

    例: select dayname(date) from pos.daywork

  2. (数据挖掘-入门-6)十折交叉验证和K近邻

    主要内容: 1.十折交叉验证 2.混淆矩阵 3.K近邻 4.python实现 一.十折交叉验证 前面提到了数据集分为训练集和测试集,训练集用来训练模型,而测试集用来测试模型的好坏,那么单一的测试是否就 ...

  3. jQuery CircleCounter的环形倒计时效果

    在线演示1 本地下载 使用jQuery插件CircleCounter生成的环形倒计时效果,这个插件使用HTML5画布生成动画效果,还不错,大家可以试试! 顺带手录制了个代码,大家不吝赐教:http:/ ...

  4. 翻译记忆软件-塔多思TRADO经典教程_2

    Trados 中文简明教程Trados 中文简明教程1. 准备工作 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ...

  5. IT创业失败案例解析 - 第一篇

    创业启示录:创业失败报告这个系列包括30多家创业公司的失败案例分析.本文就有由其中一家IT创业公司的CTO所撰写.还是那句老话,成功的故事固然非常鼓舞人心,但我们也可以从失败故事中学到很多. 以下是译 ...

  6. wepy - 转换成h5

    包地址:http://npm.taobao.org/package/wepy-web 1. npm 安装 npm install wepy-web 2.yarn 按照 yarn add wepy-we ...

  7. I/O控制方式

    I/O控制方式 在计算机系统中,CPU管理外围设备也有几种类似的方式: 1 程序查询方式 程序查询方式是早期计算机中使用的一种方式.数据在CPU和外围设备之间的传送完全靠计算机程序控制,查询方式的优点 ...

  8. geoip+php演示样例:通过ip,获取国家名称和代码

    GeoIP + PHP的使用 方法一: 下载 GeoIP 的 PHP 文件geoip.inc,保存为 geoip.inc.php http://sjolzy.cn/php/GeoIP/bak/geoi ...

  9. No value for key [org.hibernate.impl.SessionFactoryImpl 异常解决

    使用Hibernate+Spring进行CRUD操作时.出现例如以下类似异常信息: java.lang.IllegalStateException: No value for key [org.hib ...

  10. Ubuntu解压缩zip,tar,tar.gz,tar.bz2

    ZIP zip可能是目前使用得最多的文档压缩格式.它最大的优点就是在不同的操作系统平台,比如Linux, Windows以及Mac OS,上使用.缺点就是支持的压缩率不是很高,而tar.gz和tar. ...