
#include "windows.h"
#include <conio.h>
#include <stdlib.h>
#include <fstream.h>
#include <io.h>
#include <string.h>
#include <stdio.h>
#define READER 'R' //读者
#define WRITER 'W' //写者
#define INTE_PER_SEC 1000 //每秒时钟中断次数
#define MAX_THREAD_NUM 64 //最大线程数目
#define MAX_FILE_NUM 32 //最大数据文件数目
#define MAX_STR_LEN 32 //字符串长度
int readcount=0 //读者数目 (属临界资源,需以互斥方式访问)
int writecount=0 //写者数目 (属临界资源,需以互斥方式访问)
//定义3个临界资源
CRITICAL_SECTION RP_Write//临界区控制变量 (用于读者优先)
CRITICAL_SECTION cs_Write //临界区控制变量 (用于写者优先)
CRITICAL_SECTION cs_Read //临界区控制变量 (用于写者优先)
//定义线程及其 *** 作行为信息结构
struct ThreadInfo
{
int serial//线程序号
char entity //线程类别(是读者还是写者)
double delay //线程延迟
double persist //线程读写 *** 作的持续时间
}
// 读者优先――读者线程
void RP_ReaderThread(void *p) //p: 读者线程信息
{
//互斥变量
HANDLE h_Mutex
h_Mutex = OpenMutex(MUTEX_ALL_ACCESS,FALSE,"mutex_for_readcount")
//从参数中获得信息
DWORD wait_for_mutex //等待互斥变量所有权
DWORD m_delay //延迟时间
DWORD m_persist//读文件持续时间
int m_serial //线程序号
m_serial = ((ThreadInfo*)(p))->serial
m_delay = (DWORD)((ThreadInfo*)(p))->delay*INTE_PER_SEC
m_persist = (DWORD)((ThreadInfo*)(p))->persist*INTE_PER_SEC
Sleep(m_delay) //延迟等待
//输出读进程已发出读请求的提示信息
printf ("Reader thread %d sends the reading require.\n", m_serial)
//等待互斥信号,保证对readcount的互斥访问
wait_for_mutex = WaitForSingleObject(h_Mutex,-1)
readcount++
if (readcount==1) //第一个读者,等待临界区资源
EnterCriticalSection(&RP_Write)
ReleaseMutex(h_Mutex) //释放互斥信号
//输出读进程I 正在临界区中读 *** 作的提示信息
printf("Reader thread %d begins to read file.\n", m_serial)
Sleep(m_persist)
//退出线程
printf("Reader thread %d finished reading file. \n",m_serial)
//等待互斥信号,保证对readcount的互斥访问
wait_for_mutex = WaitForSingleObject(h_Mutex,-1)
readcount--
if (readcount==0) LeaveCriticalSection(&RP_Write)
ReleaseMutex(h_Mutex) //释放互斥信号
}
// 读者优先――写者线程
void RP_WriterThread(void *p) //p: 写者线程信息
{
DWORD m_delay //延迟时间
DWORD m_persist//读文件持续时间
int m_serial //线程序号
//从参数中获取信息m_serial, m_delay, m_persist ,
m_serial = ((ThreadInfo*)(p))->serial
m_delay = (DWORD)((ThreadInfo*)(p))->delay*INTE_PER_SEC
m_persist = (DWORD)((ThreadInfo*)(p))->persist*INTE_PER_SEC
//延迟等待m_delay秒;
Sleep(m_delay)
//输出信息"写线程 %d 发出写请求;
printf ("Writer thread %d sends the writing require.\n", m_serial)
//等待临界资源
EnterCriticalSection(&RP_Write)
//输出正在临界区执行写 *** 作的提示信息
//在临界区中写 *** 作,至少持续m_persist秒钟
printf("Writer thread %d begins to write file.\n", m_serial)
Sleep(m_persist)
printf("Writer thread %d finished writing file. \n",m_serial)
//释放临界资源
LeaveCriticalSection(&RP_Write)
}
// 读者优先――处理函数
void ReaderPriority(char *file) //file: 文件名
{
DWORD n_thread=0 //保存线程数目变量
DWORD thread_ID //线程ID
DWORD wait_for_all //保存――等待所有线程结束的返回值――的变量
HANDLE h_Thread[MAX_THREAD_NUM]
ThreadInfo thread_info[MAX_THREAD_NUM]
HANDLE h_Mutex=CreateMutex(NULL,FALSE,"mutex_for_readcount")
readcount=0
InitializeCriticalSection(&RP_Write) //初始化临界区
ifstream inFile
inFile.open(file)
printf("Reader Priority :\n\n")
while (inFile) { //读入每个读者、写者的信息
inFile>>thread_info[n_thread].serial
inFile>>thread_info[n_thread].entity
inFile>>thread_info[n_thread].delay
inFile>>thread_info[n_thread].persist
inFile.get()
n_thread++
}
n_thread-- //modify
for(int j=0j<(int)(n_thread)j++){
cout<<"threadinfo["<<j<<"]="<<thread_info[j].serial<<","<<thread_info[j].entity
<<","<<thread_info[j].delay<<","<<thread_info[j].persist<<endl
}
for(int i=0i<(int)(n_thread)i++){
//创建一个不被子进程继承的,使用默认大小堆栈的线程
if( thread_info[i].entity ==READER || thread_info[i].entity=='r')
h_Thread[i] = CreateThread(NULL, 0,
(LPTHREAD_START_ROUTINE)(RP_ReaderThread),
&thread_info[i],0,&thread_ID)
// thread_info[i]籍此传递给被创建线程,
// 创建标志=0, 表示线程被创建后立即执行 (其它:CREATE_SUSPEND)
//thread_ID 存放放回的线程ID标识, 可不用
else
h_Thread[i]=CreateThread(NULL, 0,
(LPTHREAD_START_ROUTINE)(RP_WriterThread),
&thread_info[i],0,&thread_ID)
}
//等待所有的线程结束
wait_for_all = WaitForMultipleObjects(n_thread, h_Thread, TRUE, -1)
printf("All reader and writer have finished operating. \n")
}
// 写者优先――读者线程
void WP_ReaderThread(void *p) //p: 读者线程信息
{
DWORD m_delay //延迟时间
DWORD m_persist//读文件持续时间
int m_serial //线程序号
m_serial = ((ThreadInfo*)(p))->serial
m_delay = (DWORD)((ThreadInfo*)(p))->delay*INTE_PER_SEC
m_persist = (DWORD)((ThreadInfo*)(p))->persist*INTE_PER_SEC
Sleep(m_delay)
printf ("Reader thread %d sends the reading require.\n", m_serial)
//延迟等待
//从参数中获取信息m_serial, m_delay, m_persist ,
//延迟等待m_delay秒;
//输出信息"读线程 %d 发出读请求的提示信息;
//打开(获取)两个互斥变量
HANDLE h_Mutex1, h_Mutex2
h_Mutex1=OpenMutex(MUTEX_ALL_ACCESS, FALSE, "mutex_for_writecount1")
/*由于写者优先,即使临界区中已有读者,也不能马上进入,
要等到没有等待写者(在写进程中,当无等待写者释放读者临界区所有权cs_Read)
因此,这里需要一个互斥信号量附加控制读者进入过程,这在读者优先算法中是不需要的。*/
DWORD wait_for_mutex1=WaitForSingleObject(h_Mutex1,-1)
EnterCriticalSection(&cs_Read)
// 用h_mutex2控制对readcount的访问
h_Mutex2 = OpenMutex(MUTEX_ALL_ACCESS,FALSE,"mutex_for_readcount2")
DWORD wait_for_mutex2 = WaitForSingleObject(h_Mutex2,-1)
readcount++
if ( readcount==1) //第一个读者要等待写者写完
EnterCriticalSection(&cs_Write)
ReleaseMutex(h_Mutex2)
LeaveCriticalSection(&cs_Read)//让其它读者可进入临界区
ReleaseMutex(h_Mutex1)
//输出某读者进程在临界区中读的信息;
//在临界区中读 *** 作,至少持续m_persist秒钟
//输出某读者进程退出临界区的信息;
printf("Reader thread %d begins to read file.\n", m_serial)
Sleep(m_persist)
//退出线程
printf("Reader thread %d finished reading file. \n",m_serial)
wait_for_mutex2 = WaitForSingleObject(h_Mutex2,-1)
readcount--
if ( readcount==0) //最后一个读者,要唤醒写者
LeaveCriticalSection(&cs_Write)
ReleaseMutex(h_Mutex2)
}
// 写者优先――写者线程
void WP_WriterThread(void *p) //p: 写者线程信息
{
DWORD m_delay //延迟时间
DWORD m_persist//读文件持续时间
int m_serial //线程序号
//从参数中获取信息m_serial, m_delay, m_persist ,
m_serial = ((ThreadInfo*)(p))->serial
m_delay = (DWORD)((ThreadInfo*)(p))->delay*INTE_PER_SEC
m_persist = (DWORD)((ThreadInfo*)(p))->persist*INTE_PER_SEC
//延迟等待m_delay秒;
Sleep(m_delay)
//输出信息"写线程 %d 发出写请求;
printf ("Writer thread %d sends the writing require.\n", m_serial)
//打开(获取)两个互斥变量h_Mutex3
HANDLE h_Mutex3
h_Mutex3 = OpenMutex(MUTEX_ALL_ACCESS,FALSE,"mutex_for_writecount3")
DWORD wait_for_mutex3 = WaitForSingleObject(h_Mutex3,-1)
writecount++
if (writecount==1) //第一个等待写者,等待读者读完
EnterCriticalSection(&cs_Read)
ReleaseMutex(h_Mutex3)
EnterCriticalSection(&cs_Write)
//进入写着临界区
//输出某写者进程在临界区中写的信息;
//至少持续m_persist秒钟
//输出某写者进程退出临界区的信息;
printf("Writer thread %d begins to write file.\n", m_serial)
Sleep(m_persist)
printf("Writer thread %d finished writing file. \n",m_serial)
LeaveCriticalSection(&cs_Write) //写者离开临界区
wait_for_mutex3 = WaitForSingleObject(h_Mutex3,-1)
writecount--
if (writecount==0) //无其它等待写者,唤醒第一个读者
LeaveCriticalSection(&cs_Read)
ReleaseMutex(h_Mutex3)
}
// 写者优先――处理函数
void WriterPriority(char *file) //file: 文件名
{
DWORD n_thread=0, thread_ID, wait_for_all
HANDLE h_Mutex1=CreateMutex(NULL,FALSE,"mutex_for_writecount1")
HANDLE h_Mutex2=CreateMutex(NULL,FALSE,"mutex_for_readcount2")
HANDLE h_Mutex3=CreateMutex(NULL,FALSE,"mutex_for_writecount3")
//用CreateMutex创建3个互斥信号量句柄h_mutex1,h_mutex2, h_mutex3
//从数据文件读入信息,创建指定数目的读写进程
//读进程处理函数 WP_ReaderThread, 写进程处理函数WP_writerThread
HANDLE h_Thread[MAX_THREAD_NUM]
ThreadInfo thread_info[MAX_THREAD_NUM]
writecount=0
readcount=0
InitializeCriticalSection(&cs_Write)
InitializeCriticalSection(&cs_Read)
//初始化临界区
ifstream inFile
inFile.open(file)
printf("Write Priority :\n\n")
while (inFile) { //读入每个读者、写者的信息
inFile>>thread_info[n_thread].serial
inFile>>thread_info[n_thread].entity
inFile>>thread_info[n_thread].delay
inFile>>thread_info[n_thread].persist
inFile.get()
n_thread++
}
n_thread--
for(int j=0j<(int)(n_thread)j++){
cout<<"threadinfo["<<j<<"]="<<thread_info[j].serial<<","<<thread_info[j].entity
<<","<<thread_info[j].delay<<","<<thread_info[j].persist<<endl
}
for(int i=0i<(int)(n_thread)i++) {
//创建一个不被子进程继承的,使用默认大小堆栈的线程
if( thread_info[i].entity ==READER || thread_info[i].entity=='r' )
h_Thread[i] = CreateThread(NULL, 0,
(LPTHREAD_START_ROUTINE)(WP_ReaderThread),
&thread_info[i],0,&thread_ID)
// thread_info[i]籍此传递给被创建线程,
// 创建标志=0, 表示线程被创建后立即执行 (其它:CREATE_SUSPEND)
//thread_ID 存放放回的线程ID标识, 可不用
else
h_Thread[i]=CreateThread(NULL, 0,
(LPTHREAD_START_ROUTINE)(WP_WriterThread),
&thread_info[i],0,&thread_ID)
}
//等待所有的线程结束
wait_for_all = WaitForMultipleObjects(n_thread, h_Thread, TRUE, -1)
printf("All reader and writer have finished operating. \n")
}
///////////////////////////////////////////////////////////
///主函数
int main(int argc, char** argv)
{
char ch
while (true)
{
//打印显示信息
printf("***************************************\n")
printf("1. Reader Priority \n")
printf("2. Writer Priority \n")
printf("3. Exit \n")
printf("***************************************\n")
printf("Enter your choice(1,2,or 3): \n")
do {
ch = (char)_getch()
} while (ch != '1' &&ch != '2' &&ch!='3')
system("cls")
if(ch=='3') return 0
else if(ch=='1') ReaderPriority("thread.dat")
else if(ch=='2') WriterPriority("thread.dat")
//结束
printf("\nPress any key to continute:")
_getch()
system("cls")
}
return 1
}
当多个线程访问一个独占性共享资源时,可以使用“临界区”对象。任一时刻只有一个线程可以拥有临界区对象,拥有临界区的线程可以访问被保护起来的资源或代码段,其他希望进入临界区的线程将被挂起等待,直到拥有临界区的线程放弃临界区时为止,这样就保证了不会在同一时刻出现多个线程访问共享资源。CCriticalSection类的用法非常简单,步骤如下:
定义CCriticalSection类的一个全局对象(以使各个线程均能访问),如CCriticalSection critical_section;
在访问需要保护的资源或代码之前,调用CCriticalSection类的成员Lock()获得临界区对象: critical_section.Lock()
在线程中调用该函数来使线程获得它所请求的临界区。如果此时没有其它线程占有临界区对象,则调用Lock()的线程获得临界区;否则,线程将被挂起,并放入到一个系统队列中等待,直到当前拥有临界区的线程释放了临界区时为止。
访问临界区完毕后,使用CCriticalSection的成员函数Unlock()来释放临界区:critical_section.Unlock()
再通俗一点讲,就是线程A执行到critical_section.Lock()语句时,如果其它线程(B)正在执行critical_section.Lock()语句后且critical_section. Unlock()语句前的语句时,线程A就会等待,直到线程B执行完critical_section. Unlock()语句,线程A才会继续执行。
呵呵,当初我学多线程时也遇到过这样的问题,也是输出的结果每次都不一样。后来我找到原因了---都是多核惹得祸。我猜你的电脑应该也是多核的。单核的cpu在处理多线程时每次只能执行一跳指令,也就是说无论你的程序有多少个线程,每一时刻执行的也只是一个线程里的代码,cpu会轮流给每个线程分配时间片,时间片分配到哪个线程头上,哪个线程里的代码就执行。但是多核cpu就不一样了,他可以同时执行多个线程里的代码,这才是真正的“多线程”。所以你那段程序,在单核的电脑上跑应该是没有问题的,但是在多核cpu的电脑上出现的结果就会有很大的随机性。
就你贴的那张图来说,左边的运行时恰好是这样的,首先cpu1执行你主线程里的代码 在终端输出Now another thread has been created. ID =,但是由于多个cpu是同时进行的,而这时cpu2已经开始执行ThreadProc里的代码,也要开始向终端输出字符,而你的屏幕只有一个,恰好这时cpu1的时间片被移走了,所以cpu2开始执行ThreadProc里的代码向屏幕上输出,直到打完I am from a thread 17后,恰好cpu2的时间片被移走了,这时cpu1接着向屏幕打dwThreadId的值,这就出现了4660.接着又是cpu2执行完ThreadProc中剩余的代码又打了几行。
右边的这个程序运行时,恰好就是cpu1执行主线程代码输出完后,cpu2再执行线程函数中代码,符合你的预期。
但是,关键问题在于,你无法预测每个cpu的时间片分配。所以,要得到你想要的输出结果就属于随机事件了。
对与多核cpu 上的程序同步问题,最好不要用信号量,互斥量,事件对象,因为它们都属于内核对象,都是对一个cpu而言的。其他的cpu根本不会理睬你设置的这些东西。另外你的WaitForSingleObject (hThread, INFINITE)也是在一个cpu里等待线程函数返回,对cpu2没有任何作用。
建议你用临界区(Critical Section)来实现多线程同步,因为临界区不是内核对象,他只是在进程内存中一块区域,无论有多少个cpu,任何时刻只能有一个线程访问这块内存区域,只需将你打印的部分放到临界区里就行了。
#include "stdafx.h"
#include <windows.h>
#include <iostream>
using namespace std
CRITICAL_SECTION g_cs
DWORD WINAPI ThreadProc(
LPVOID lpParameter // thread data
)
{
int i=0
while (i<20)
{
EnterCriticalSection(&g_cs)
cout<<"I am from a thread"<<" "<<i++<<endl
LeaveCriticalSection(&g_cs)
}
return 0
}
int main(int argc, char* argv[])
{
HANDLE hThread
DWORD dwThreadId
InitializeCriticalSection(&g_cs)
// 创建一个线程
hThread = ::CreateThread (
NULL, // 默认安全属性
NULL, // 默认堆栈大小
ThreadProc, // 线程入口地址(执行线程的函数)
NULL, // 传给函数的参数
0, // 指定线程立即运行
&dwThreadId)// 返回线程的ID号
EnterCriticalSection(&g_cs)
cout<<"Now another thread has been created. ID ="<<dwThreadId <<"\n"
LeaveCriticalSection(&g_cs)
// 等待新线程运行结束
::WaitForSingleObject (hThread, INFINITE)
::CloseHandle (hThread)
return 0
}
ps: kdzhy2008推荐的 侯捷 译的《win32多线程程序设计》确实是本好书,虽然是97年出版的,但是很多东西对现在还是很有启迪的。
希望对你能有所帮助,呵呵~~
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)