创建一个线程,自然有一个对应的系统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. Ejb in action(一)——开篇介绍

    从今天開始.我们共同来学习JavaEE中一个很重要的规范:Ejb. 既然您已经找到了这篇文章.就说明您至少已经对分布式开发有个大体上的概念了,之前没了解过也没关系,正好通过咱们的共同学习,一起来了解它 ...

  2. c语言訪问excel

    直接通过格式化读取文件就可实现,见附件

  3. Spring+DBUnit+H2----项目单元测试

    http://yugouai.iteye.com/blog/1879337 今天够郁闷的,早上调好的代码,到中午调试不同了,分析不出问题,H2的JDBC报错:org.h2.jdbc.JdbcSQLEx ...

  4. 破解无线网络密码-BT3如何使用2

    本教程只作学习和交流使用,任何其它商用与本人无关, 在开始教程之前, 首先需要用到几个软件,感兴趣的朋友看完贴子后可以去百度搜一下下载地址, 虚拟机: VMware Workstation 6.5 正 ...

  5. Genymotion 下载安装常见错误一条龙

    Genymotion 安卓模拟器确实比安卓原生的模拟器快,但是除了快就找不到其他优点了... 曾经尝试在VM虚拟机内的Ubuntu系统里面再运行Genymotion的,主要原因是要FQ去下载一些东西, ...

  6. 新浪微博SSO登陆机制(转载)

    原文地址: http://www.cnblogs.com/AloneSword/p/3840548.html 最近在使用sina微博时,经常性交替使用 weibo.com 和 t.sina.cm.cn ...

  7. 解决mysql下区分表名大小写的问题

    MySQL在Linux下采用 rpm方式安装后默认是: 数据库名与表名\表的别名\变量名是严格区分大小写 1.用root帐号登录,/etc/ mysql/my.cnf中的[mysqld]后添加lowe ...

  8. Hibernate(十五)注解

    一.Hibernate注解 使用注解的方式来注释类和属性,从而完成对象和关系的映射 二.步骤 三.注解标签 四.查询

  9. locate 命令(转)

    原文:http://www.cnblogs.com/peida/archive/2012/11/12/2765750.html locate 让使用者可以很快速的搜寻档案系统内是否有指定的档案.其方法 ...

  10. [Done]mysql in (#{list}) 只能查询/删除第一条的问题

    数据如下(注意age是int类型): sql如下(注意是#不是$): java代码: Mybatis日志(只返回一笔记录): 直接在mysql中执行(age是int类型,注意参数带引号,确认jdbc是 ...