创建一个线程,自然有一个对应的系统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. 打通Fedora19的vsftpd服务

    Fedora19默认vsftpd也没安,安装界面里也没地方让我选,这一点不如以前的版本人性化.没办法只有自己来了,倒也不费事. 1.下载vsftpd rpm安装包 我是从http://rpmfind. ...

  2. (算法)Trapping Rain Water I

    题目: Given n non-negative integers representing an elevation map where the width of each bar is 1, co ...

  3. 【Python学习笔记】

    目录 语法 if语句 while循环 字符串常用操作 列表 只读列表元组 字典的使用 语法 if语句 示例 #!/usr/bin/env python user = 'jack' passwd = ' ...

  4. Android 四大组件之 Service(二)

    这里主要介绍Service组件的使用. 1.定义一个继承Service的子类 如下: package com.service; import android.app.Service; import a ...

  5. php之快速入门学习-1

    貌似和JSP差不多! PHP 脚本可以放在文档中的任何位置. PHP 脚本以 <?php 开始,以 ?> 结束: <?php// PHP 代码?> PHP 文件的默认文件扩展名 ...

  6. Electron 调用系统Office软件

    通过打开本地空白文件的方式从而打开Office办公软件 const shell = require('electron').shell; const path = require('path'); s ...

  7. Windows 无法启动MongoDB服务 错误1067:进程意外终止

    1:Windows 无法启动MongoDB服务 错误1067:进程意外终止 2:解决方法: 进入MongoDB安装目录\data\将此文件夹下的mongod.lock删除(网友提供解决方案,实验之后貌 ...

  8. 解决-bash: fork: retry: Resource temporarily unavailable (修改最大线程数)

    错误提示的本质是Linux操作系统无法创建更多进程,导致出错.因此要解决这个问题需要修改Linux允许创建更多的进程. 方案一: cat /etc/security/limits.conf echo ...

  9. Redis学习(8)-redis持久化

    内存(兔子):高效,断电数据丢失 硬盘(乌龟):读写速度慢于内存的,断电数据依旧存在 持久化:把数据保存在硬盘上 关系型数据库:MySQL-持久化: 任何操作都是硬盘上,断电以后,硬盘上数据还在. 非 ...

  10. AWK中的OFS的问题

    echo a b c d |awk '{OFS = ":";print $0}' 我的理解是应该把输出显示为如下的方式 a:b:c:d dan但执行的结果不是这样的 a b c d ...