操作系统实验:Windows环境下线程的同步与互斥

本文描述了如何在Windows环境下使用CreateThread创建和撤销线程,包括传递参数的方法。接着探讨了进程同步,通过Semaphore和Mutex实现线程间的同步和互斥,并通过实验展示了临界区对象和互斥对象的使用。最后,选做部分通过信号量和互斥量解决了经典的生产者-消费者问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

实验要求:

1、进程的创建与撤销

(1)会使用CreateThread()、ExitThread(0)等系统调用创建和撤销线程;

(2)尝试向线程所在函数传递一个参数;

(3)尝试向线程所在函数传递多个参数。

(4)记录程序的运行结果,分析出现各种不同情况的原因,并记录。

2、进程的同步

(1)使用CreateThread()创建两个线程p和q。

(2)学会使用CreateSemaphore() 创建一个同步对象,返回一个对象句柄;OpenSemaphore(()打开并返回一个已存在的同步对象句柄(给信号量赋初值);ReleaseSemaphore()释放对同步对象的占用(V操作); WaitForSingleObject()在指定时间内等待指定对象为可用。(P操作)

(3)使用Semaphore完成三个线程的同步p---à主线程;q---à主线程。   P-->Q-->主线程

3、临界区的相关操作

(1)学会使用InitializeCriticalSection() 对临界区对象进行初始化; EnterCriticalSection()等待占有临界区的使用权。 (P操作); LeaveCriticalSection ()释放对临界区的使用权。(V操作); DeleteCriticalSection ()删除与临界区有关的所有系统资源。

(2)学会使用CreateMutex() 创建一个互斥对象,返回一个对象句柄;OpenMutex()打开并返回一个已存在的互斥对象句柄(给信号量赋初值);ReleaseMutex()释放对互斥对象的占用(V操作); WaitForSingleObject()在指定时间内等待指定对象为可用。(P操作)。

(3)完成p和q两个线程的互斥

假设p和q两线程共享变量COUNT(COUNT的初值为5),p和q分别包含以下程序段:

p:{

  R1=COUNT;

  R1=R1+1;

  COUNT=R1; }

q:{

  R2=COUNT;

  R2=R2+1;

  COUNT=R2; }

4、(选做)用上面的同步、互斥工具完成“生产者-消费者”问题。

生产者:

P(empty);

P(mutex);

xxxx;

V(mutex);

V(full);

实验过程:

1.(1)// diyicishiyan.cpp : Defines the entry point for the console application.

#include "stdafx.h"

#include "diyicishiyan.h"

#ifdef _DEBUG

#define new DEBUG_NEW

#undef THIS_FILE

static char THIS_FILE[] = __FILE__;

#endif

/

// The one and only application object

CWinApp theApp;

using namespace std;

void ThreadName1();

static HANDLE hHandle1=NULL;

DWORD dwThreadID1;

int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])

{

int nRetCode = 0;

hHandle1=CreateThread((LPSECURITY_ATTRIBUTES)NULL,

0,

(LPTHREAD_START_ROUTINE) ThreadName1,

(LPVOID)NULL,

0,

&dwThreadID1);

Sleep(5000);

CloseHandle(hHandle1);

ExitThread(0);

return nRetCode;

}

void ThreadName1()

{

printf("Thread is Running!\n");

}

(2)

//传一个参数

// diyicishiyan.cpp : Defines the entry point for the console application.

//

#include "stdafx.h"

#include "diyicishiyan.h"

#ifdef _DEBUG

#define new DEBUG_NEW

#undef THIS_FILE

static char THIS_FILE[] = __FILE__;

#endif

/

// The one and only application object

CWinApp theApp;

using namespace std;

/*struct paramts{

 int x;

 int y;

};*/

void ThreadName1(int *a);

//void HappyThreadmore(struct paramts *xy);

static HANDLE hHandle1=NULL;

DWORD dwThreadID1;

int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])

{

int nRetCode = 0;

int a=5;

  /*struct paramts xy;

   xy.x=55;

   xy.y=66;*/

hHandle1=CreateThread((LPSECURITY_ATTRIBUTES)NULL,

0,

(LPTHREAD_START_ROUTINE) ThreadName1,

//(LPTHREAD_START_ROUTINE) HappyThreadmore,

(LPVOID) a,

//(LPVOID) &xy,

0,

&dwThreadID1);

Sleep(5000);

for(int i=0;i<3;i++){

printf("这里是主线程!!!\n");

}

CloseHandle(hHandle1);

ExitThread(0);

return nRetCode;

}

void ThreadName1(int *a)

{

printf("Thread is Running!\n");

printf("a====%d\n",a);

}

/*void HappyThreadmore(struct paramts *xy)

{

printf("这里是线程HappyThread,x=%d,y=%d\n",xy->x,xy->y);

Sleep(5000);

}

*/

(3) //传两个参数的

// diyicishiyan.cpp : Defines the entry point for the console application.

//

#include "stdafx.h"

#include "diyicishiyan.h"

#ifdef _DEBUG

#define new DEBUG_NEW

#undef THIS_FILE

static char THIS_FILE[] = __FILE__;

#endif

/

// The one and only application object

CWinApp theApp;

using namespace std;

struct paramts{

 int x;

 int y;

};

void HappyThreadmore(struct paramts *xy);

//void ThreadName1();

static HANDLE hHandle1=NULL;

DWORD dwThreadID1;

int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])

{

int nRetCode = 0;

  struct paramts xy;

   xy.x=55;

   xy.y=66;

hHandle1=CreateThread((LPSECURITY_ATTRIBUTES)NULL,

0,

//(LPTHREAD_START_ROUTINE) ThreadName1,

(LPTHREAD_START_ROUTINE) HappyThreadmore,

//(LPVOID)a,

(LPVOID) &xy,

0,

&dwThreadID1);

//Sleep(50);

for(int i=0;i<10;i++){

printf("这里是主线程!!!\n");

}

CloseHandle(hHandle1);

ExitThread(0);

return nRetCode;

}

void ThreadName1(int *a)

{

printf("Thread is Running!\n");

printf("a====%d\n",a);

}

void HappyThreadmore(struct paramts *xy)

{

printf("这里是线程HappyThread,x=%d,y=%d\n",xy->x,xy->y);

Sleep(5000);

}

  1. 在实验中发现同一个程序每次的运行结果并不总是一样,通过询问老师以及复习操作系统理论知识,最终明白这种现象是正常的,这恰好说明线程之间的运行是不可复现的。

2.

// diyicishiyan.cpp : Defines the entry point for the console application.

//

#include "stdafx.h"

#include "diyicishiyan.h"

#ifdef _DEBUG

#define new DEBUG_NEW

#undef THIS_FILE

static char THIS_FILE[] = __FILE__;

#endif

/

// The one and only application object

CWinApp theApp;

using namespace std;

static HANDLE h1;

static HANDLE hHandle1=NULL;

void func();

int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])

{

int nRetCode = 0;

DWORD dwThreadID1;

DWORD dRes,err;

hHandle1=CreateSemaphore(NULL,0,1,"SemaphoreName1");

if(hHandle1==NULL) printf("Semaphore Create Fail!\n");

else printf("Semaphore Create Success!\n");

hHandle1=OpenSemaphore(SYNCHRONIZE|SEMAPHORE_MODIFY_STATE,

NULL,

"SemaphoreName1");

if(hHandle1==NULL) printf("Semaphore Open Fail!\n");

else printf("Semaphore Open Success!\n");

h1=CreateThread((LPSECURITY_ATTRIBUTES)NULL,

0,

(LPTHREAD_START_ROUTINE)func,

(LPVOID)NULL,

0,&dwThreadID1);

if(h1==NULL) printf("Thread1 Create Fail!\n");

else printf("Thread1 Create Success!\n");

dRes = WaitForSingleObject(hHandle1,INFINITE);

err=GetLastError();

printf("WaitForSingleObject err = %d\n",err);

if(dRes==WAIT_TIMEOUT) printf("TIMEOUT! dRes=%d\n",dRes);

else if(dRes ==WAIT_OBJECT_0) printf("WAIT_OBJECT!dRes = % d\n",dRes);

else if(dRes ==WAIT_ABANDONED)

printf("WALT_ABANDONED!dRes = % d\n",dRes);

else printf("dRes = % d \n",dRes);

CloseHandle(h1);

CloseHandle(hHandle1);

ExitThread(0);

return nRetCode;

}

void func()

{

BOOL rc;

DWORD err;

printf("Now In Thread!\n");

rc=ReleaseSemaphore(hHandle1,1,NULL);

err=GetLastError();

printf("ReleaseSemaphore err = % d\n",err);

if(rc==0) printf("Semaphore Release Fail!\n");

else printf("Semaphore Release Success!rc=%d\n",rc);

}

3.

(1)// diercishiyan.cpp : Defines the entry point for the console application.

//

#include "stdafx.h"

#include "diercishiyan.h"

#ifdef _DEBUG

#define new DEBUG_NEW

#undef THIS_FILE

static char THIS_FILE[] = __FILE__;

#endif

/

// The one and only application object

CWinApp theApp;

using namespace std;

static int count=0;

static HANDLE h1;

static HANDLE h2;

LPCRITICAL_SECTION hCriticalSection;

CRITICAL_SECTION Critical;

void func1();

void func2();

int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])

{

int nRetCode = 0;

printf("hhhhhhhhhhhh\n");

DWORD dwThreadID1,dwThreadID2;

hCriticalSection=&Critical;

InitializeCriticalSection(hCriticalSection);

      h1=CreateThread((LPSECURITY_ATTRIBUTES)NULL,

0,

(LPTHREAD_START_ROUTINE)func1,

(LPVOID)NULL,

0,

&dwThreadID1);

  if(h1==NULL) printf("Thread1 Create Fail!\n");

else printf("Thread1 Create Success!\n");

  h2=CreateThread((LPSECURITY_ATTRIBUTES)NULL,

0,

(LPTHREAD_START_ROUTINE)func2,

(LPVOID)NULL,

0,

&dwThreadID2);

  if(h2==NULL) printf("Thread2 Create Fail!\n");

else printf("Thread2 Create Success!\n");

Sleep(100);

CloseHandle(h1);

CloseHandle(h2);

DeleteCriticalSection(hCriticalSection);

ExitThread(0);

return nRetCode;

}

void func1()

{

int r1;

Sleep(1);

EnterCriticalSection(hCriticalSection);

r1=count;

r1=r1+1;

count=r1;

printf("count in thresd1=%d\n",count);

LeaveCriticalSection(hCriticalSection);

}

void func2()

{

int r2;

EnterCriticalSection(hCriticalSection);

r2=count;

r2=r2+1;

count=r2;

printf("count in thread2=%d\n",count);

LeaveCriticalSection(hCriticalSection);

}

(2)

// diercishiyan.cpp : Defines the entry point for the console application.

//

#include "stdafx.h"

#include "diercishiyan.h"

#ifdef _DEBUG

#define new DEBUG_NEW

#undef THIS_FILE

static char THIS_FILE[] = __FILE__;

#endif

/

// The one and only application object

CWinApp theApp;

using namespace std;

static int count=0;

static HANDLE mutex;

static HANDLE t1;

static HANDLE t2;

void pfunc();

void qfunc();

int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])

{

int nRetCode = 0;

printf("hhhhhhhhhhhh\n");

DWORD dwThreadID1,dwThreadID2;

mutex=CreateMutex(NULL,FALSE,"Mutex");//创建互斥对象(安全属性、初始权限、信号量名字)

mutex=OpenMutex(SYNCHRONIZE,NULL,"Mutex");//打开互斥对象(访问属性、继承属性、信号量名字)

 t1=CreateThread((LPSECURITY_ATTRIBUTES)NULL,

0,

(LPTHREAD_START_ROUTINE)pfunc,

(LPVOID)NULL,

0,

&dwThreadID1);

  if(t1==NULL) printf("Thread1 Create Fail!\n");

else printf("P thread is Created Success!\n");

  t2=CreateThread((LPSECURITY_ATTRIBUTES)NULL,

0,

(LPTHREAD_START_ROUTINE)qfunc,

(LPVOID)NULL,

0,

&dwThreadID2);

  if(t2==NULL) printf("Thread2 Create Fail!\n");

else printf("Q thread is Created Success!\n");

Sleep(100);

CloseHandle(t1);

CloseHandle(t2);

CloseHandle(mutex);

ExitThread(0);

return nRetCode;

}

void pfunc()

{

WaitForSingleObject(mutex,INFINITE);

count=count+1;

printf("count in P thread=%d\n",count);

ReleaseMutex(mutex);

}

void qfunc()

{

WaitForSingleObject(mutex,INFINITE);

count=count+1;

printf("count in Q thread=%d\n",count);

ReleaseMutex(mutex);

}

4.

#include <Windows.h>

#include <iostream>

#include <vector>

using namespace std;

const int BUFFER_SIZE = 5;

vector<int> buffer(BUFFER_SIZE);  

int buffer_index = 0;  

HANDLE buffer_mutex;  

HANDLE empty_semaphore;  

HANDLE full_semaphore;  

DWORD WINAPI Producer(LPVOID lpParam)

{

    while (true)

    {

        int item = rand() % 100;  

        WaitForSingleObject(empty_semaphore, INFINITE);  

        WaitForSingleObject(buffer_mutex, INFINITE);

        buffer[buffer_index] = item;  

        buffer_index = (buffer_index + 1) % BUFFER_SIZE;  

        cout << "Produced item " << item << endl;

        ReleaseMutex(buffer_mutex);  

        ReleaseSemaphore(full_semaphore, 1, NULL);  

        Sleep(1000);  

    }

    return 0;

}

DWORD WINAPI Consumer(LPVOID lpParam)

{

    while (true)

    {

        WaitForSingleObject(full_semaphore, INFINITE);

        WaitForSingleObject(buffer_mutex, INFINITE);  

        int item = buffer[(buffer_index + BUFFER_SIZE - 1) % BUFFER_SIZE];  

        buffer_index = (buffer_index + BUFFER_SIZE - 1) % BUFFER_SIZE;  

        cout << "Consumed item " << item << endl;

        ReleaseMutex(buffer_mutex);  // release the lock on the buffer

        ReleaseSemaphore(empty_semaphore, 1, NULL);  // signal that a slot in the buffer is empty

        Sleep(5000);  // wait for some time before consuming the next item

    }

    return 0;

}

int main()

{

buffer_mutex = CreateMutex(NULL, FALSE, NULL);  

    empty_semaphore = CreateSemaphore(NULL, BUFFER_SIZE, BUFFER_SIZE, NULL);  

    full_semaphore = CreateSemaphore(NULL, 0, BUFFER_SIZE, NULL);  

HANDLE producer_thread = CreateThread(NULL, 0, Producer, NULL, 0, NULL);  

HANDLE consumer_thread = CreateThread(NULL, 0, Consumer, NULL, 0, NULL);  

   

    WaitForSingleObject(producer_thread, INFINITE);

    WaitForSingleObject(consumer_thread, INFINITE);

       CloseHandle(producer_thread);

    CloseHandle(consumer_thread);

    CloseHandle(buffer_mutex);

    CloseHandle(empty_semaphore);

    CloseHandle(full_semaphore);

    return 0;

}

实验总结:

1.说明本次实验后你对使用信号量对象(Semaphore)、临界区对象(CriticalSection)和互斥对象(Mutex)的理解说明。

信号量对象(Semaphore)

信号量对象信号允许多个线程同时使用共享资源,这与操作系统中的PV操作相同。它指出了同时访问共享资源的线程最大数目。它允许多个线程在同一时刻访问同一资源,但是需要限制在同一时刻访问此资源的最大线程数目。在用CreateSemaphore()创建信号量时即要同时指出允许的最大资源计数和当前可用资源计数。一般是将当前可用资源计数设置为最大资源计数,每增加一个线程对共享资源的访问,当前可用资源计数就会减1,只要当前可用资源计数是大于0的,就可以发出信号量信号。但是当前可用计数减小到0时则说明当前占用资源的线程数已经达到了所允许的最大数目,不能在允许其他线程的进入,此时的信号量信号将无法发出。线程在处理完共享资源后,应在离开的同时通过ReleaseSemaphore()函数将当前可用资源计数加1。在任何时候当前可用资源计数决不可能大于最大资源计数。

临界区对象(Critical Section)
   保证在某一时刻只有一个线程能访问数据的简便办法。在任意时刻只允许一个线程对共享资源进行访问。如果有多个线程试图同时访问临界区,那么在有一个线程进入后其他所有试图访问此临界区的线程将被挂起,并一直持续到进入临界区的线程离开。临界区在被释放后,其他线程可以继续抢占,并以此达到用原子方式操作共享资源的目的。 

互斥量(Mutex) 

斥量跟临界区很相似,只有拥有互斥对象的线程才具有访问资源的权限,由于互斥对象只有一个,因此就决定了任何情况下此共享资源都不会同时被多个线程所访问。当前占据资源的线程在任务处理完后应将拥有的互斥对象交出,以便其他线程在获得后得以访问资源。互斥量比临界区复杂。因为使用互斥不仅仅能够在同一应用程序不同线程中实现资源的安全共享,而且可以在不同应用程序的线程之间实现对资源的安全共享。

2.说明信号量对象(Semaphore)、临界区对象(CriticalSection)和互斥对象(Mutex)的使用场合及他们的区别?

 号量:为控制一个具有有限数量用户资源而设计。Semaphore通过使用计数器来控制对共享资源的访问。 如果计数器大于0,则允许访问。 如果为0,则拒绝访问。计数器所计数的是允许访问共享资源的许可。 因此,要访问资源,必须从信号量中授予线程许可。

临界区:通过对多线程的串行化来访问公共资源或一段代码,速度快,适合控制数据访问。

互斥量:为协调共同对一个共享资源的单独访问而设计的。

3.在该实验中你有哪些没有解决的问题?

对互斥对象的使用还不是特别熟练,生产者消费者问题一旦涉及多生产者多消费者多缓冲区就有点吃力。

4通过该实验对理解操作系统理论有何帮助?

通过实际操作,对课上学过的知识印象更加深刻。比如这句话:在Windows系统中进程是资源的拥有者,线程是系统调用的基本单位。进程创建后,其主线程也随即被创建。

5.关于本实验,你有什么建议?

与理论知识一起辅助学习,在动手实操的过程中时刻温习理论知识。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

七月初七淮水竹亭~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值