//示例代码:
CStringArray g_ArrString;
UINT __cdecl ThreadProc(LPVOID lpParameter)
{
int startIdx = (int)lpParameter;
for (int idx = startIdx; idx < startIdx+100; ++idx) {
CString str;
str.Format(_T("%d"), idx);
g_ArrString.Add(str);
}
return 0;
}
void CThreadTestDlg::OnBnClickedBtn()
{
for (int idx = 1; idx <= 50; ++idx) {
AfxBeginThread(ThreadProc, (LPVOID)(idx*10));
}
}
void CThreadTestDlg::OnBnClickedPrintBtn()
{
CString strCount;
INT_PTR nCount = g_ArrString.GetCount();
strCount.Format(_T("%d"), nCount);
MessageBox(strCount);
for (INT_PTR idx = 0; idx < nCount; ++idx) {
OutputDebugString(g_ArrString.GetAt(idx));
}
}
///////////////////////////////////////////////////////////////////////////////
①、Mutex(互斥器)
使用方法:
1、创建一个互斥器:CreateMutex;
2、打开一个已经存在的互斥器:OpenMutex;
3、获得互斥器的拥有权:WaitForSingleObject、WaitForMultipleObjects 等一类等待的函数……(可能造成阻塞);
4、释放互斥器的拥有权:ReleaseMutex;
5、关闭互斥器:CloseHandle;
HANDLE ghMutex = NULL;
CStringArray g_ArrString;
UINT __cdecl ThreadProc(LPVOID lpParameter)
{
int startIdx = (int)lpParameter;
for (int idx = startIdx; idx < startIdx+100; ++idx) {
CString str;
str.Format(_T("%d"), idx);
DWORD dwWaitResult = WaitForSingleObject(ghMutex, INFINITE);
switch (dwWaitResult)
{
case WAIT_ABANDONED:
case WAIT_OBJECT_0:
g_ArrString.Add(str);
ReleaseMutex(ghMutex);
break;
}
//g_ArrString.Add(str);
}
return 0;
}
void CThreadTestDlg::OnBnClickedBtn()
{
ghMutex = CreateMutex(NULL, FALSE, NULL);
for (int idx = 1; idx <= 50; ++idx) {
AfxBeginThread(ThreadProc, (LPVOID)(idx*10));
}
}
void CThreadTestDlg::OnBnClickedPrintBtn()
{
CString strCount;
INT_PTR nCount = g_ArrString.GetCount();
strCount.Format(_T("%d"), nCount);
MessageBox(strCount);
for (INT_PTR idx = 0; idx < nCount; ++idx) {
OutputDebugString(g_ArrString.GetAt(idx));
}
CloseHandle(ghMutex);
}
※ 命名标准:Mutex 可以跨进程使用,所以其名称对整个系统而言是全局的,所以命名不要过于普通,类似:Mutex、Object 等。
最好想一些独一无二的名字等!
固有特点(优点+缺点):
1、是一个系统核心对象,所以有安全描述指针,用完了要 CloseHandle 关闭句柄,这些是内核对象的共同特征;
2、因为是核心对象,所以执行速度会比 Critical Sections 慢几乎100倍的时间(当然只是相比较而言);
3、因为是核心对象,而且可以命名,所以可以跨进程使用;
4、Mutex 使用正确的情况下不会发生死锁;
5、在“等待”一个 Mutex 的时候,可以指定“结束等待”的时间长度;
6、可以检测到当前拥有互斥器所有权的线程是否已经退出!Wait……函数会返回:WAIT_ABANDONED
===================================================
②、Semaphores(信号量)
租车例子的比喻很恰当!
使用方法:
1、创建一个信号量:CreateSemaphore;
2、打开一个已经存在的信号量:OpenSemaphore;
3、获得信号量的一个占有权:WaitForSingleObject、WaitForMultipleObjects 等一类等待的函数……(可能造成阻塞);
4、释放信号量的占有权:ReleaseSemaphore;
5、关闭信号量:CloseHandle;
HANDLE ghSemaphore = NULL;
UINT __cdecl ThreadProc(LPVOID lpParameter)
{
int startIdx = (int)lpParameter;
CString strOut;
while(TRUE) {
DWORD dwWaitResult = WaitForSingleObject(ghSemaphore, 0);
switch (dwWaitResult)
{
case WAIT_OBJECT_0:
strOut.Format(_T("Thread %d: wait succeeded !"), GetCurrentThreadId());
OutputDebugString(strOut);
ReleaseSemaphore(ghSemaphore, 1, NULL);
break;
case WAIT_TIMEOUT:
strOut.Format(_T("Thread %d: wait timed out !"), GetCurrentThreadId());
OutputDebugString(strOut);
break;
}
}
return 0;
}
void CThreadTestDlg::OnBnClickedBtn()
{
ghSemaphore = CreateSemaphore(NULL, 10, 10, NULL);
for (int idx = 1; idx <= 20; ++idx) {
AfxBeginThread(ThreadProc, (LPVOID)(idx*10));
}
}
void CThreadTestDlg::OnBnClickedPrintBtn()
{
CloseHandle(ghSemaphore);
}
※ 命名标准:Semaphores 可以跨进程使用,所以其名称对整个系统而言是全局的,所以命名不要过于普通,类似:Semaphore、Object 等。
最好想一些独一无二的名字等!
固有特点(优点+缺点):
1、是一个系统核心对象,所以有安全描述指针,用完了要 CloseHandle 关闭句柄,这些是内核对象的共同特征;
2、因为是核心对象,所以执行速度稍慢(当然只是相比较而言);
3、因为是核心对象,而且可以命名,所以可以跨进程使用;
4、Semaphore 使用正确的情况下不会发生死锁;
5、在“等待”一个 信号量 的时候,可以指定“结束等待”的时间长度;
6、非排他性的占有,跟 Critical Sections 和 Mutex 不同,这两种而言是排他性占有,
即:同一时间内只能有单一线程获得目标并拥有操作的权利,而 Semaphores 则不是这样,
同一时间内可以有多个线程获得目标并操作!
所以,这里面问大家一个问题,如果还是用信号量的方式去做之前向 CStringArray 中添加节点的同步可以吗?
===================================================
③、Event Objects(事件)
Event 方式是最具弹性的同步机制,因为他的状态完全由你去决定,不会像 Mutex 和 Semaphores 的状态会由类似:
WaitForSingleObject 一类的函数的调用而改变,所以你可以精确的告诉 Event 对象该做什么事?以及什么时候去做!
使用方法:
1、创建一个事件对象:CreateEvent;
2、打开一个已经存在的事件对象:OpenEvent;
3、获得事件的占有权:WaitForSingleObject 等函数(可能造成阻塞);
4、释放事件的占有权(设置为激发(有信号)状态,以让其他等待中的线程苏醒):SetEvent;
5、手动置为非激发(无信号)状态:ResetEvent
6、关闭事件对象句柄:CloseHandle;
固有特点(优点+缺点):
1、是一个系统核心对象,所以有安全描述指针,用完了要 CloseHandle 关闭句柄,这些是内核对象的共同特征;
2、因为是核心对象,所以执行速度稍慢(当然只是相比较而言);
3、因为是核心对象,而且可以命名,所以可以跨进程使用;
4、通常被用于 overlapped I/O 或被用来设计某些自定义的同步对象。
#include <windows.h>
#include <stdio.h>
#define THREADCOUNT 4
HANDLE ghGlobalWriteEvent;
HANDLE ghReadEvents[THREADCOUNT];
DWORD WINAPI ThreadProc(LPVOID);
void CreateEventsAndThreads(void)
{
HANDLE hThread;
DWORD i, dwThreadID;
// Create a manual-reset event object. The master thread sets
// this to nonsignaled when it writes to the shared buffer.
ghGlobalWriteEvent = CreateEvent(
NULL, // default security attributes
TRUE, // manual-reset event
TRUE, // initial state is signaled
TEXT("WriteEvent") // object name
);
if (ghGlobalWriteEvent == NULL)
{
printf("CreateEvent failed (%d)\n", GetLastError());
return;
}
else if ( GetLastError() == ERROR_ALREADY_EXISTS )
{
printf("Named event already exists.\n");
return;
}
// Create multiple threads and an auto-reset event object
// for each thread. Each thread sets its event object to
// signaled when it is not reading from the shared buffer.
for(i = 0; i < THREADCOUNT; i++)
{
// Create the auto-reset event
ghReadEvents[i] = CreateEvent(
NULL, // no security attributes
FALSE, // auto-reset event
TRUE, // initial state is signaled
NULL); // object not named
if (ghReadEvents[i] == NULL)
{
printf("CreateEvent failed (%d)\n", GetLastError());
return;
}
hThread = CreateThread(NULL,
0,
ThreadProc,
&ghReadEvents[i], // pass event handle
0,
&dwThreadID);
if (hThread == NULL)
{
printf("CreateThread failed (%d)\n", GetLastError());
return;
}
}
}
void WriteToBuffer(VOID)
{
DWORD dwWaitResult, i;
// Reset ghGlobalWriteEvent to nonsignaled, to block readers
if (! ResetEvent(ghGlobalWriteEvent) )
{
printf("ResetEvent failed (%d)\n", GetLastError());
return;
}
// Wait for all reading threads to finish reading
dwWaitResult = WaitForMultipleObjects(
THREADCOUNT, // number of handles in array
ghReadEvents, // array of read-event handles
TRUE, // wait until all are signaled
INFINITE); // indefinite wait
switch (dwWaitResult)
{
// All read-event objects were signaled
case WAIT_OBJECT_0:
// TODO: Write to the shared buffer
printf("Main thread writing to the shared buffer...\n");
break;
// An error occurred
default:
printf("Wait error: %d\n", GetLastError());
ExitProcess(0);
}
// Set ghGlobalWriteEvent to signaled
if (! SetEvent(ghGlobalWriteEvent) )
{
printf("SetEvent failed (%d)\n", GetLastError());
ExitProcess(0);
}
// Set all read events to signaled
for(i = 0; i < THREADCOUNT; i++)
if (! SetEvent(ghReadEvents[i]) )
{
printf("SetEvent failed (%d)\n", GetLastError());
return;
}
}
void CloseEvents()
{
int i;
for( i=0; i < THREADCOUNT; i++ )
CloseHandle(ghReadEvents[i]);
CloseHandle(ghGlobalWriteEvent);
}
void main()
{
int i;
// TODO: Create the shared buffer
// Create the events and THREADCOUNT threads to read from the buffer
CreateEventsAndThreads();
// Write to the buffer three times, just for test purposes
for(i=0; i < 3; i++)
WriteToBuffer();
// Close the events
CloseEvents();
}
DWORD WINAPI ThreadProc(LPVOID lpParam)
{
DWORD dwWaitResult;
HANDLE hEvents[2];
hEvents[0] = *(HANDLE*)lpParam; // thread's read event
hEvents[1] = ghGlobalWriteEvent; // global write event
dwWaitResult = WaitForMultipleObjects(
2, // number of handles in array
hEvents, // array of event handles
TRUE, // wait till all are signaled
INFINITE); // indefinite wait
switch (dwWaitResult)
{
// Both event objects were signaled
case WAIT_OBJECT_0:
// TODO: Read from the shared buffer
printf("Thread %d reading from buffer...\n",
GetCurrentThreadId());
break;
// An error occurred
default:
printf("Wait error: %d\n", GetLastError());
ExitThread(0);
}
// Set the read event to signaled
if (! SetEvent(hEvents[0]) )
{
printf("SetEvent failed (%d)\n", GetLastError());
ExitThread(0);
}
return 1;
}
===================================================
※※※ 小作业:
MFC中同样对 Mutex、Semaphores、Event 进行了封装,所以尝试使用 CMutex、CSemaphore、CEvent 等类进行线程间的同步将更为方便!
------------------------------------- End -------------------------------------------
- vc++高级班之多线程篇[6]---线程间的同步机制①
①.线程同步的必要性: int g_Num = 0; UINT __cdecl ThreadProc(LPVOID lpParameter) { for (int idx = 0; idx &l ...
- iOS开发多线程篇 04 —线程间的通信
iOS开发多线程篇—线程间的通信 一.简单说明 线程间通信:在1个进程中,线程往往不是孤立存在的,多个线程之间需要经常进行通信 线程间通信的体现 1个线程传递数据给另1个线程 在1个线程中执行完特定任 ...
- vc++高级班之窗口篇[4]---让程序只运行一个实例
大家都看过或者使用过类似只运行一个实例的程序,比如:QQ游戏.部分浏览器 等等! 让一个程序只运行一个实例的方法有多种,但是原理都类似,也就是在程序创建后,有窗口的程序在窗口创建前, 检查系统中是 ...
- Linux 多线程 - 线程异步与同步机制
Linux 多线程 - 线程异步与同步机制 I. 同步机制 线程间的同步机制主要包括三个: 互斥锁:以排他的方式,防止共享资源被并发访问:互斥锁为二元变量, 状态为0-开锁.1-上锁;开锁必须由上锁的 ...
- iOS开发多线程篇—创建线程
iOS开发多线程篇—创建线程 一.创建和启动线程简单说明 一个NSThread对象就代表一条线程 创建.启动线程 (1) NSThread *thread = [[NSThread alloc] in ...
- Java并发之线程间的同步协作与通信协作
1,Monitor监视器与syncrhoized实现原理 1.1:Monitor Monitor是一个同步工具,相当于操作系统中的互斥量(mutex),即值为1的信号量. 它内置与每一个Object对 ...
- iOS开发多线程篇 03 —线程安全
iOS开发多线程篇—线程安全 一.多线程的安全隐患 资源共享 1块资源可能会被多个线程共享,也就是多个线程可能会访问同一块资源 比如多个线程访问同一个对象.同一个变量.同一个文件 当多个线程访问同一块 ...
- swift开发多线程篇 - NSThread 线程相关简单说明(一些使用和注意点)
一 说明 本文涉及代码可以从https://github.com/HanGangAndHanMeimei/Code地址获得. 二 NSThread的基本使用和创建 1)基本用法(主线程|当前线程) 1 ...
- java多线程5:线程间的通信
在多线程系统中,彼此之间的通信协作非常重要,下面来聊聊线程间通信的几种方式. wait/notify 想像一个场景,A.B两个线程操作一个共享List对象,A对List进行add操作,B线程等待Lis ...
随机推荐
- [JUC-2]AbstractQueuedSynchronizer源码分析
AbstactQueuedSynchronizer的基本数据结构 AbstractQueuedSynchronizer的基本数据结构为Node,关于Node,JDK作者写了详细的注释,这里我大致总结几 ...
- DirectX11 With Windows SDK--02 顶点/像素着色器的创建、顶点缓冲区
前言 由于在Direct3D 11中取消了固定管线,要想绘制图形必须要了解可编程渲染管线的流程,一个能绘制出图形的渲染管线最少需要有这两个可编程着色器:顶点着色器和像素着色器. 本章会直接跳过渲染管线 ...
- Spark源码剖析 - SparkContext的初始化(六)_创建和启动DAGScheduler
6.创建和启动DAGScheduler DAGScheduler主要用于在任务正式交给TaskSchedulerImpl提交之前做一些准备工作,包括:创建Job,将DAG中的RDD划分到不同的Stag ...
- impala系列: 时间函数
--=======================时间函数--======================= --当前时间戳now()current_timestamp() --当前时间戳相对于 li ...
- Windows 常用的 CMD 命令
Ø 简介 本文主要记录 Windows 下一些常用的 CMD 命令,俗话说好记性不如烂笔头. 1. help 查看 cmd 命令帮助信息(包括所有的 cmd 命令) 2. 设置 cmd 进入 ...
- 学习 vue 源码 -- 响应式原理
概述 由于刚开始学习 vue 源码,而且水平有限,有理解或表述的不对的地方,还请不吝指教. vue 主要通过 Watcher.Dep 和 Observer 三个类来实现响应式视图.另外还有一个 sch ...
- 七、文件IO——I/O处理方式和文件锁
7.1 I/O 处理方式 7.1.1 I/O处理的五种模型 阻塞I/O模型 若所调用的 I/O 函数没有完成相关的功能就会使进程挂起,直到相关数据到达才会返回.如 终端.网络设备的访问. 非阻塞模型 ...
- 【演变】Ajax(AjAj)到WebSocket
提出问题:A => 服务器 => B B端浏览器如何知道服务器有A发来的数据? 解决方案: 第1种:频繁轮询 间隔1秒B向服务器讨要数据,就算数据为空.[ ...
- solr集群SolrCloud(solr+zookeeper)windows搭建
SolrCloud是什么 参考 solrCloud官网介绍 http://lucene.apache.org/solr/guide/6_6/solrcloud.html Apache Solr 可以设 ...
- PHP+MySql+Bootstrap实现用户界面数据的删除、修改与批量选择删除——实例操作
第一步:在数据库中建立要操作的信息表 如下图: 第二步:实现对该信息表中数据的删除功能 代码如下:main(主页面) <!DOCTYPE html><html> < ...