• 实验一:线程创建与撤销 2.1.1 实验目的 (1)熟悉Windows系统提供的线程创建与撤销系统调用。 (2)掌握Windows系统环境下线程创建与撤销方法。 2.1.2 实验准备知识 1.线程创建 CeateThread()完成线程创建...

    实验一:线程的创建与撤销

    2.1.1 实验目的

    (1)熟悉Windows系统提供的线程创建与撤销系统调用。

    (2)掌握Windows系统环境下线程的创建与撤销方法。

    2.1.2 实验准备知识

    1.线程的创建

    CeateThread()完成线程的创建。它调用进程的地址空间上创建一个线程,执行指定的函数,并返回创建的线程的句柄。

    原型:

    HANDLE CreateThread(

    LPSECURITY_ATTRIBUTES IpThreadAttributes,

    DWORD dwStackSize,

    LPSECURITY_START_ROUTINE IpStartAddress,

    LPVOID Ipparameter,

    DWORD dwCreationFlags,

    LPDWORD IpThreadld

    );

    参数说明:

    (1)lpThreadAttributes:为线程指定安全属性.为NULL时,线程得到一个默认的安全描述符.

    (2)dwStackSize:线程堆栈的大小.其值为0时,其大小与调用该线程的线程堆栈大小相同.

    (3)lpStartAddress:指定线程要执行的函数.

    (4)lpparameter:函数中要传递的参数.

    (5)dwCreationFlags:指定线程创建后所处的状态.若为CRRATE_SUSPENDED,CRRATE_SUSPENDED,表示创建后出于挂起状态,用ResumeThread()激活线程才可以执行.若该值为0,表示线程创建后立即执行.

    (6)lpThreadId:用一个32位的变量接受系统返回的线程标识符.若该值设为NULL,系统不返回线程标识符.

    返回值:

    如果线程创建成功,将返回线程的句柄;如果失败,系统返回NULL,可以调用函数GetLastError查询失败的原因.

    用法举例:

    ​ static HANDLE hHandle=NULL;//用于存储线程返回句柄的变量

    ​ DWORD dwThreadlD;//用于存储线程标识符的变量

    ​ hHandle = CreateThread((LPSECURITY_ATTRIBUTES) NULL,

    ​ 0,

    ​ (LPTHREAD_START_ROUTINE) 函数名,

    ​ (LPVOID) NULL,

    ​ 0,

    ​ &ThreadlD

    );

    2.撤销线程

    ExiThread()用于撤销当前进程。

    原型:

    ​ VOID ExiThread(DWORD dwExitCode);//线程返回码

    参数说明:

    dwExitCode:指定线程返回码,可以调用 GetExitCodeThread()查询返回码的含义。

    返回值:

    没有返回值。

    用法举例:

    ​ ExitThread(0);

    3.终止线程

    TerminateThread()用于终止当前线程。该函数与ExitThread()的区别在于,ExiThread()在撤销线程时将该线程所拥有的资源全部归还给系统,二终止线程不归还。

    原型:

    BOOL TerminateThread(

    HANDLE hHandle, //线程句柄

    DWORD dwExitCode); //线程返回码

    参数说明:

    (1)hThread:要终止线程的线程句柄.

    (2)dwExitCode:指定线程返回码,可以调用GetExitCodeThread()查询返回码的含义.

    返回值:

    函数调用成功,将返回一个非0值;若失败,返回0,可以调用函数GetLastError()查询失败的原因.

    4.挂起线程

    Sleep()用于挂起当前正在执行的线程.

    原型:

    VOID Sleep(DWORD dwMilliseconds);

    参数说明:

    dwMilliseconds;指定挂起时间,单位为ms(毫秒).

    返回值:

    该函数没有返回值.

    5.关闭句柄

    函数CloseHandle()用于关闭已打开的对象的句柄,其作用与释放动态申请的内存空间类似,这样可以释放系统资源,使线程安全运行.

    原型:

    BOOL CloseHandle(HANDLE hObject);

    参数说明:

    hObject:已打开对象的句柄.

    返回值:

    如果函数调用成功,则返回值为非0值;如果函数调用失败,则返回值为0.若要得到更多的错误信息,调用函数GetLastError()查询.

    2.1.3实验内容

    使用系统调用CreatThread()创建一个子线程,并在子线程中显示;Thread is Running!.为了能让用户清楚地看到线程的运行情况,使用Sleep()使线程挂起5s,之后使用ExitThread(0)撤销进程.

    2.1.4实验要求

    能正确使用CreatThread(),ExitThread()及Sleep()等系统调用,进一步理解进程与线程理论.

    2.1.5实验指导

    本实验在WindowsXP,Microsoft Visual C++ 6.0环境下实现,利用Windows SDK提供的API完成程序的功能.实验在Windows XP环境下安装由于WindowsXP,Microsoft Visual C++ 6.0是一个集成开发环境,其中包含了Windows SDK 所有工具和定义,所以安装了WindowsXP,Microsoft Visual C++ 6.0后不用特意安装SDK.试验中所有的API是操作系统提供的用来进行应用程序开发的系统功能接口.

    (1)首先启动安装好的,Microsoft Visual C++ 6.0.

    (2)在,Microsoft Visual C++ 6.0环境下选择File->new命令,然后在Project选项卡中选择Win32 Console Application建立一个控制台工程文件.

    (3)由于CreatThread()等函数是Microsoft Windows操作系统的系统调用,因此,在下图中选择An application that supports MFC,之后单击Finish按钮

    image

    2.1.6实验总结

    在Windows系统中进程是资源的拥有者,线程是系统调用的单位.进程创建后,其主线程也随即被创建.在该实验中,有创建了一个名为 hHandle1的子线程,该子线程与主线程并发的被系统调度.为了能让用户清楚地看到线程的运行情况,在主线程创建了子线程之后,将主线程挂起5s,以确保子线程能够运行完毕,之后调用ExitThread(0)将所有线程撤销.线程运行如下图所示.

    image

    2.1.7 源程序

    #include "xianchengchuangjian.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 ThreadName()
    {
    	printf("Thread is Running!\n");
    }
    static HANDLE hHandle1=NULL; 
    DWORD ThreadID1;
    
    int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
    {
    	int nRetCode = 0;
    	
        
    
    	hHandle1=CreateThread((LPSECURITY_ATTRIBUTES) NULL,
    		0,
    		(LPTHREAD_START_ROUTINE) ThreadName,
    		(LPVOID)NULL,
    		0,
    		&ThreadID1);
    	Sleep(5000);
    	CloseHandle(hHandle1);
    	ExitThread(0);
    	
    	return nRetCode;
    
    
    	// initialize MFC and print and error on failure
    	if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0))
    	{
    		// TODO: change error code to suit your needs
    		cerr << _T("Fatal Error: MFC initialization failed") << endl;
    		nRetCode = 1;
    	}
    	else
    	{
    		// TODO: code your application's behavior here.
    		CString strHello;
    		strHello.LoadString(IDS_HELLO);
    		cout << (LPCTSTR)strHello << endl;
    	}
    	return nRetCode;
    
    }
    void ThreaName1()
    {
    printf("Thread is Runing!\n");
    }
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    展开全文
  • 随机生成N组非负整数列表,然后创建N个线程,分别用N种不同的排序算法对列表进行排序 N必须大于2 进入图形模式,沿垂直方向把屏幕分成N个区域,每个排序线程用一个区域,动态显示排序过程。 求大神给出相关代码
  • 操作系统实验3 线程 ■目的 深入理解线程线程在调度执行和内存空间等方面的特点, 并掌握线程与进程的区别。掌握POSIX规范中pthread create( )函数的功能和使用方法。 ■实验前准备 阅读参考资料,了解线程...

    操作系统一实验3
    线程
    ■目的
    深入理解线程及线程在调度执行和内存空间等方面的特点, 并掌握线程与进程的区别。掌握POSIX规范中pthread create( )函数的功能和使用方法。
    ■实验前准备
    阅读参考资料,了解线程的创建等相关系统调用

    实验准备:
    线程创建函数:pthread_create(&pthread_id[i] , NULL , thread_work , ( void* ) & ( index[ i ] ) );
    第一个参数:把创建出来的线程的编号传给pthread_id[ i ];
    第二个参数:一般为NULL;
    第三个参数:简单理解为该线程执行什么样的工作。
    第四个参数:一般来说把第四个参数传到第三个参数中。

    #include<stdio.h>
    #include<sys/types.h>
    #include<unistd.h>
    #include<ctype.h>
    #include<pthread.h>
    
    #define MAX_THREAD 3
    unsigned long long main_counter,counter[MAX_THREAD];
    void* thread_work(void*);
    int main(int argc,char*argv[])
    {
    	int i,rtn,ch;
    	int index[MAX_THREAD];
    	
    	pthread_t pthread_id[MAX_THREAD] = {0};
    	for(int i = 0 ; i < MAX_THREAD;i++)
    	{
    		index[i] = i;
    		pthread_create(&pthread_id[i],NULL,thread_work,(void *)&(index[i]));//把整数类型的地址转换成void类型的地址,这样能传到		void* thread_work(void*)中。
    			}
    	do
    	{
    		unsigned long long sum  = 0;
    		for(int i = 0 ; i < MAX_THREAD;i++)
    		{
    			sum+=counter[i];
    			printf("%llu ",counter[i]);
    			printf("%llu/%llu",main_counter,sum);
    		}
    	}while((ch = getchar()) != 'q');
    	return 0;
    }
    
    void *thread_work(void *p)
    {
    	int thread_num = *((int *)p);
    	for(;;)
    	{
    		counter[thread_num]++;
    		main_counter++;
    	}
    }
    

    运行结果:
    在这里插入图片描述

    程序解读:
    (1)程序执行之后,进程有一个主线程,三个子线程。程序执行完,主线程结束,三个子线程会强行结束。
    (2)之所以最后main_counter和三个counter[ i ]不同,是因为三个线程共享一个main_counter,导致并发执行
    main_counter++,但是每个counter[ i ]是正常加,所以main_counter比三个counter[ i ]相加要小。
    (3)三个counter[ i ]占用CPU的效率近似相同,但并不完全相同,与时间片轮转有关。

    正确代码:

    #include<stdio.h>
    #include<stdlib.h>
    #include<sys/types.h>
    #include<unistd.h>
    #include<ctype.h>
    #include<pthread.h>
    
    #define MAX_THREAD  3  //线程个数
    
    //初始化锁
    static pthread_mutex_t    mutex = PTHREAD_MUTEX_INITIALIZER;
    
    unsigned long long   main_counter,counter[MAX_THREAD]={0};
    
    void* thread_worker(void*  arg)
    {
        //先将void* 转为 int* 再赋值
        int  thread_num = *(int*)arg;
        //释放内存
        free((int*)arg);
        for(;;)
        {
            //加锁
            pthread_mutex_lock(&mutex);
            counter[thread_num]++;    //本线程的counter 加 1
            main_counter++;
            //解锁
            pthread_mutex_unlock(&mutex);
            //sleep(3);
        }
    }
    
    int main(int argc,char* argv[])
    {
        int                 i,rtn,ch;
        pthread_t           pthread_id[MAX_THREAD] =  {0};  //存放线程
    
        int                 *param;
        for(i=0;i<MAX_THREAD;i++)
        { 
            //申请内存临时保存参数
            param = (int*)malloc(sizeof(int));
            *param = i;
            pthread_create(&pthread_id[i],NULL,thread_worker,param);
        }
    
        do
        {
            //加锁
            pthread_mutex_lock(&mutex);
            unsigned long long   sum = 0;
            for(i=0;i<MAX_THREAD;i++)
            {
                sum += counter[i];
                printf("No.%d: %llu\n",i,counter[i]);
            }
            printf("%llu/%llu\n",main_counter,sum);
            //解锁
            pthread_mutex_unlock(&mutex);
        }while((ch = getchar())!='q');
    
        //销毁锁资源
        pthread_mutex_destroy(&mutex);
        return 0;
    }
    
    

    这里解释一下两个加锁的地方:
    1.第一个加锁的地方在工作函数,如果工作函数不加锁,3个子线程会竞争一个main_counter资源,导致数据会和3个counter之和不同,main_counter比较小。
    2.第二个加锁的地方在do_while循环里面,这里如果不加虽然主线程已经加过3个counter,但是子线程还在运行,main_counter还在增加。导致main_counter比较大

    展开全文
  • 1.创建线程 #include<stdio.h> #include<stdlib.h> #include<pthread.h> /* 声明结构体 */ struct member { int num; char *name; }; /* 定义线程pthread */ static void * pthread(void *...

    1.创建线程

    #include<stdio.h>
    #include<stdlib.h>
    #include<pthread.h>
    
    /* 声明结构体 */
    struct member
    {
        int num;
        char *name;
    };     
    
    /* 定义线程pthread */
    static void * pthread(void *arg)       
    {
        struct member *temp;
        
        /* 线程pthread开始运行 */
        printf("pthread start!\n");
        
        /* 令主线程继续执行 */
        sleep(2);
        
        /* 打印传入参数 */
        temp = (struct member *)arg;      
        printf("member->num:%d\n",temp->num);
        printf("member->name:%s\n",temp->name);
        
        return NULL;
    }
    
    /* main函数 */
    int main(int agrc,char* argv[])
    {
        pthread_t tidp;
        struct member *b;
    
        /* 为结构体变量b赋值 */
        b = (struct member *)malloc(sizeof(struct member));           
        b->num=1;
        b->name="mlq";              
    
        /* 创建线程pthread */
        if ((pthread_create(&tidp, NULL, pthread, (void*)b)) == -1)
        {
            printf("create error!\n");
            return 1;
        }
        
        /* 令线程pthread先运行 */
        sleep(1);
        
        /* 线程pthread睡眠2s,此时main可以先执行 */
        printf("mian continue!\n");
        
        /* 等待线程pthread释放 */
        if (pthread_join(tidp, NULL))                  
        {
            printf("thread is not exit...\n");
            return -2;
        }
        
        return 0;
    }
    

    2.撤销线程

    Linux线程可以取消其他线程,被取消的线程会被退出
    线程本身可以设置不被其他线程取消

    把线程状态设置为PTHREAD CANCEL-DISABLE的实例代码:

    #include <pthread.h>
    #include <stdio.h>
    #include <string.h>
     
    pthread_t tid1, tid2, tid3;
     
    void* th_reader1(void *p)
    {
    	int i = 1;
    	int oldstate = -1;
    	pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldstate);
        for(; i <= 5; i++)
    	{
    		printf("func[%s]: 第 %d 秒\n", __FUNCTION__, i);
    		sleep(1);
    	}
    	pthread_exit( (void *)0 );
    }
     
    int main()
    {
        void *ret1, *ret2, *ret3;		
    	
        printf("start thread reader 1\n");
        pthread_create(&tid2, NULL, th_reader1, NULL);  //创建 读 线程1
        
        sleep(2);
        pthread_cancel(tid2);  //发送取消信号
    	
        pthread_join(tid2, &ret2);
     
        return 0;
    }
    
    

    运行结果

    # ./a.out 
    start thread reader 1
    func[th_reader1]: 第 1 秒
    func[th_reader1]: 第 2 秒
    func[th_reader1]: 第 3 秒
    func[th_reader1]: 第 4 秒
    func[th_reader1]: 第 5 秒
    

    把线程状态设置为PTHREAD CANCEL-DISABLE后,再设置为PTHREAD-CANCEL-ENABLE的实例代码:

    #include <pthread.h>
    #include <stdio.h>
    #include <string.h>
     
    pthread_t tid1, tid2, tid3;
     
    void* th_reader1(void *p)
    {
    	int i = 1;
    	int oldstate = -1;
    	pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldstate);
        for(; i <= 5; i++)
    	{
    		printf("func[%s]: 第 %d 秒\n", __FUNCTION__, i);
    		sleep(1);
    		if(4 == i)
    		{
    			pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldstate);
    			printf("thread cancel PTHREAD-CANCEL-ENABLE \n");
    			pthread_testcancel();						
    		}
    	}
    	pthread_exit( (void *)0 );
    }
     
    int main()
    {
        void *ret1, *ret2, *ret3;		
    	
        printf("start thread reader 1\n");
        pthread_create(&tid2, NULL, th_reader1, NULL);  //创建 读 线程1
        
        sleep(2);
        pthread_cancel(tid2);//发送取消信号
    	
        pthread_join(tid2, &ret2);
     
     
        return 0;
    }
    
    

    运行结果:

    运行结果:
    
    # ./a.out 
    start thread reader 1
    func[th_reader1]: 第 1 秒
    func[th_reader1]: 第 2 秒
    func[th_reader1]: 第 3 秒
    func[th_reader1]: 第 4 秒
    thread cancel PTHREAD-CANCEL-ENABLE 
    

    3.终止线程

    #include <pthread.h>
    
    #include <stdio.h>
    
    #include <stdlib.h>
    
    void *func1(void * f1)
    
    {
    
        puts("我是第一个线程!我采用return的方式结束自己。");
    
        return (void*)770880;
    
    }
    
    void *func2(void * f2)
    
    {
    
        puts("我是第二个线程!我采用pthread_exit的方式结束自己。");
    
        pthread_exit((void*)1314);
    
    }
    
    void *func3(void * f3)
    
    {
    
        puts("我是第三个线程!我采用pthread_cancel的方式结束自己。");
    
        while (1)//可能会发生线程执行完但是主线程还没有开始调用pthread_cancel函数。
    
            pthread_testcancel();//主动设置取消点
    
        return (void*)520;
    
    }
    
    int main(void)
    
    {
    
        pthread_t pth[3];
    
        void *i;
    
        pthread_create((pth + 0), NULL, func1, NULL);
    
        pthread_join(pth[0], (void**)&i);
    
        printf("线程一的退出状态:i = %d.\n", (int)i);
    
        pthread_create((pth + 1), NULL, func2, NULL);
    
        pthread_join(pth[1], (void**)&i);
    
        printf("线程二的退出状态:i = %d.\n", (int)i);
    
        pthread_create((pth + 2), NULL, func3, NULL);
    
        pthread_cancel(pth[2]);//杀死
    
        pthread_join(pth[2], (void**)&i);
    
        printf("线程三的退出状态:i = %d.\n", (int)i);
    
        return 0;
    
    }
    

    4.挂起线程

    /*
    *主线程创建子线程(当前子线程为stop停止状态),5秒后主线程唤醒子线程,
    *10秒后主线程挂起子线程。15秒后主线程再次唤醒子线程,20秒后主线程执行完毕,
    *等待子线程退出。
    */
    #include <stdio.h>
    #include <string.h>
    #include <pthread.h>
     
    #define	RUN 	1
    #define STOP	0
     
    pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER;
    pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
     
    int status = STOP;
     
    void *thread_function(void)
    {
    	static int i = 0;
    	while(1)
    	{
    		pthread_mutex_lock(&mut);
    		while(!status)
    		{
    			pthread_cond_wait(&cond, &mut);
    		}
    		pthread_mutex_unlock(&mut);
    		printf("child pthread %d\n", i++);
    		if (i == 20)
    		{
    			break;
    		}
    		sleep(1);
    	}
    }
     
    void thread_resume()
    {
    	if (status == STOP)
    	{
    		pthread_mutex_lock(&mut);
    		status = RUN;
    		pthread_cond_signal(&cond);
    		printf("pthread run!\n");
    		pthread_mutex_unlock(&mut);
    	}
    	else
    	{
    		printf("pthread run already\n");
    	}
    }
     
    void thread_pause()
    {
    	if (status == RUN)
    	{
    		pthread_mutex_lock(&mut);
    		status = STOP;
    		printf("thread stop!\n");
    		pthread_mutex_unlock(&mut);
    	}
    	else
    	{
    		printf("pthread pause already\n");
    	}
    }
     
    int main()
    {
    	int err;
    	static int i = 0;
    	pthread_t child_thread;
    	
    	if (pthread_mutex_init(&mut, NULL) != 0)
    	{
    		printf("mutex init error\n");
    	}
    	if (pthread_cond_init(&cond, NULL) != 0)
    	{
    		printf("cond init error\n");
    	}
    	
    	err = pthread_create(&child_thread,NULL,(void *)thread_function,NULL);
    	if (err != 0)
    	{
    		printf("can't create thread:%s\n", strerror(err));
    	}
    	
    	while(1)
    	{
    		printf("father pthread %d\n", i++);
    		sleep(1);
    		if (i == 5)
    		{
    			thread_resume();
    		}
    		if (i == 10)
    		{
    			thread_pause();
    		}
    		if (i == 15)
    		{
    			thread_resume();
    		}
    		if (i == 20)
    		{
    			break;
    		}
    	}
    	if (0 == pthread_join(child_thread, NULL))
    	{
    		printf("child thread is over\n");
    	}
    	
    	return 0;
    }
    

    5.关闭句柄

    #include <stdio.h>
    #include <string.h>
    #include<unistd.h>
    #include<signal.h>
    #include<sys/types.h>
    #include<sys/wait.h>
    int main()
    {
    pid_t pid;
    int status;
    if(!(pid= fork())){
    printf("Hi I am child process!\n");
    sleep(10);
    return 0;
    }
    else{
    printf("send signal to child process (%d) \n",pid);
    sleep(1);
    kill(pid ,SIGABRT); 
    wait(&status);
    if(WIFSIGNALED(status))
    printf("chile process receive signal %d\n",WTERMSIG(status));
    }
    return 0;
    }
    
    

    运行结果:

    send signal to child process(3170)
    Hi I am child process!
    child process receive signal 6
    
    展开全文
  • 操作系统实验报告:线程创建和撤销; 基于Windows xp;需要提前安装虚拟机;内含源程序和程序运行结果。
  • 操作系统实验三——线程的互斥 ** 一、 实验目的 (1)熟练掌握windows系统环境下线程创建和撤销。 (2)熟悉windows系统提供的线程互斥API。 (3)使用windows系统提供的线程互斥API解决实际问题。 二、 实验准备...

    **

    操作系统实验三——线程的互斥

    **

    一、 实验目的

    (1)熟练掌握windows系统环境下线程的创建和撤销。
    (2)熟悉windows系统提供的线程互斥API。
    (3)使用windows系统提供的线程互斥API解决实际问题。

    二、 实验准备

    相关API函数介绍
    1)临界区对象
    临界区对象(CriticalSection)包括:
    (1)初始化临界区(InitializeCriticalSection())
    (2)进入临界区(EnterCriticalSection())
    (3)退出临界区(LeaveCriticalSection())
    (4)删除临界区(DeleteCriticalSection())
    等API函数。
    (1)初始化临界区
    InitializeCriticalSection()用于初始化临界区对象。
    原型:

    VOID InitializeCriticalSection(
    LPCRITICAL_SECTION  lpCriticalSection 
       );
    

    参数说明:
    lpCriticalSection:指出临界区对象的地址。
    返回值:该函数没有返回值。
    用法举例:

    LPCRITCAL_SECTION hCriticalSection;
    CRITICAL_SECTION Critical;
    hCriticalSection=&Critical;
    InitializeCriticalSection(hCriticalSection);
    

    (2)进入临界区
    EnterCriticalSection()等待进入临界区的权限,当获得该权限后进入临界区。原型:

    VOID EnterCriticalSection(
    LPCRITICAL_SECTION lpCriticalSection 
    );
    

    参数说明:
    lpCriticalSection:指出临界区对象的地址。
    返回值:该函数没有返回值。
    用法举例:

    LPCRITICAL_SECTION hCriticalSection;
    CRITICAL_SECTION Critical; 
    hCriticalSection=&Critical;
     EnterCriticalSection(hCriticalSection); 
    

    (3)退出临界区
    CriticalSectionLeaveCriticalSection()释放临界区的使用权限。
    原型:

     VOID LeaveCriticalSection( 
     LPCRITICAL_SECTION lpCriticalSection 
    )

    参数说明:
    lpCriticalSection:指出临界区对象的地址。
    返回值:该函数没有返回值
    用法举例:

    LPCRITICAL_SECTION hCriticalSection;
    CRITICAL_SECTION Critical;
    hCriticalSection=&Critical;
    LeaveCriticalSection(hCriticalSection);
    

    (4)删除临界区
    DeleteCriticalSection()删除与临界区有关的所有系统资源。
    原型:

    VOID DeleteCriticalSection(
    LPCRITICAL_SECTION  lpCriticalSection 
    );
    

    参数说明:
    lpCriticalSection:指出临界区对象的地址。
    返回值:该函数没有返回值。
    用法举例:

    LPCRITICAL_SECTION hCriticalSection;
    CRITICAL_SECTION Critical;
    hCriticalSection=&Critical;
    DeleteCriticalSection(hCriticalSection)
    

    2)互斥对象
    互斥对象(Mutex)包括:
    (1)创建互斥对象(CreateMutex())
    (2)打开互斥对象(OpenMutex())
    (3)释放互斥对象(ReleaseMutex())
    等API函数。
    (1)创建互斥对象
    CreateMutex()用于创建一个互斥对象。
    原型:

    HANDLE CreateMutex(
    LPSECURITY_ATTRIBUTES lpMutexASributes,
    BOOL bInitialOwner,
    LPCTSTR lpName 
    );
    

    参数说明:
    lpMutexASributes:指定安全属性,为NULL时,信号量得到一个,默认的安全描述符。
    bInitialOwner:指定初始的互斥对象。如果该值为TRUE并且互斥对象已经存在,则调用线程获得互斥对象的所有权,否则调用线程不能获得互斥对象的所有权。想要知道互斥对象是否已经存在,参⻅返回值说明。
    lpName:给出互斥对象的名字,标识哪个线程当前拥有这个互斥对象。
    返回值:互斥对象创建成功,将返回该互斥对象的句柄。如果给出的互斥对象是系统已经存在的互斥对象,将返回这个已存在互斥对象的句柄。如果失败,系统返回NULL,可以调用函数GetLastError()查询失败的原因。
    用法举例:

    static HANDLE  hHandle1=NULL; //常⻅一个名为"MutexName1"的互斥对象hHandle1=CreateMutex(NULL,FALSE, "MutexName1");
    

    (2)打开互斥对象
    OpenMutex()用于打开一个互斥对象。
    原型:

    HANDLE OpenMutex(
    DWORD dwDesiredAccess,
    BOOL bInheritHandle,
    LPCTSTR lpName
     );
    

    参数说明:
    指明系统安全属性支持的对互斥对象所有可能的访问。如果系统安全属性不支持,则不能获得对互斥对象访问权。
    dwDesireAccess:指出发开后要对互斥对象进行何种访问,具体描述如表2-4所示。

    在这里插入图片描述
    bInheritHandle:指出返回信号量的句柄是否可以继承。
    IpName :给出信号量的名字。
    返回值:互斥对象打开成功,将返回该互斥对象的句柄;如果失败,系统返回NULL,可以调用函数GetLastError()查询失败的原因。
    用法举例:

    static HANDLE hHandle1 = NULL//打开一个名为"MutexName1"的互斥对象hHandle=OpenMutex(SYNCHRONIZE,NULL,"MutexName1");
    

    (3)释放互斥对象
    ReleaseMutex()用于释放互斥对象。
    原型:

    BOOL ReleaseMutex(
    HANDLE hMutex;
    );
    

    参数说明:
    hMutex:Mutex对象的句柄。CreateMutex()和OpenMutex()函数返回该句柄。
    返回值:如果成功,将返回一个非0值;如果失败系统将返回0,可以调用函数 GetLastError()查询失败的原因。
    用法举例:

    static HANDLE hHandle1=NULL;
     BOOL rc;
     rc= ReleaseMutex(hHandle1) 
    

    三、 实验内容

    (1) 实验内容
    完成两个子线程之间的互斥。在主线程中使用系统调用 CreateThread()创建两个子线程,并使两个子线程互斥的使用全局变量 count。
    (2) 主要代码

    using namespace std;
    static int count=5;
    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;
    
    	DWORD dwThreadID1,dwThreadID2;
    
    	hCriticalSection=&Critical;      //将指向临界区对象的指针指向临界区
    	InitializeCriticalSection(hCriticalSection);  //初始化临界区
    
    	h1=CreateThread((LPSECURITY_ATTRIBUTES)NULL,
    		0,
    		(LPTHREAD_START_ROUTINE)func1,
    		(LPVOID)NULL,
    		0,&dwThreadID1);    //创建线程func1
    
    	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);    //创建线程func2
    
    	if(h2==NULL) printf("Thread2 create FAIL!\n");
    	else printf("Thread2 create Success!\n");
    
    	Sleep(1000);
    	CloseHandle(h1);
        CloseHandle(h2);
    	DeleteCriticalSection(hCriticalSection);  //删除临界区
    	ExitThread(0);
    
    	return nRetCode;
    }
    
    void func2(){
    	int r2;
    
    	EnterCriticalSection(hCriticalSection);    //进入临界区
    	r2=count;
    	_sleep(100);
    	r2=r2+1;
    	count=r2;
    	printf("count in func2=%d\n",count);
    
    	LeaveCriticalSection(hCriticalSection);     //退出临界区
    }
    
    void func1(){
    	int r1;
    
    	 EnterCriticalSection(hCriticalSection);    //进入临界区
    	r1=count;
    	_sleep(100);
    	r1=r1+1;
    	count=r1;
    	printf("count in func1=%d\n",count);
    
    	LeaveCriticalSection(hCriticalSection);     //退出临界区
    }
    

    四、实验结果与总结

    该试验完成了两个子线程之间的互斥。

    在这里插入图片描述
    若去掉互斥对象,观察全局变量count的变化,了解互斥对象的作用,进一步理解线程的互斥。本试验也可以使用互斥对象(Mutex)来完成两个线程的互斥,互斥对象(Mutex)的使用方法与信号量对象相似。线程互斥访问全局变量count 。

    展开全文
  • 实验二:线程的同步 ...​ 熟悉windows系统提供的线程同步API(是WINDOWS提供给应用程序与操作系统的接口) ​ 使用windows系统提供的线程同步API解决实际问题 2.2.2 实验准备知识:相关API函数介绍 ...

    实验二:线程的同步

    2.2.1 实验目的

    ​ 进一步掌握windows系统环境下线程的创建和撤销

    ​ 熟悉windows系统提供的线程同步API(是WINDOWS提供给应用程序与操作系统的接口)

    ​ 使用windows系统提供的线程同步API解决实际问题

    2.2.2 实验准备知识:相关API函数介绍

    2.2.2.1等待对象 (一个)

    ​ 等待对象(wait fuctions)函数包括等待一个对象 (WaitForSingleObject())和等待多对象 (WaitForMultipleObject())两个API函数。等待一个对象WaitForMultipleObject()用于等 待一个对象。他等待的对象可以为以下对象之一。

    ​ Change notification:变化通知。

    ​ Console input:控制台输入。

    ​ Events:事件。

    ​ Job:作业。

    ​ Mutex:互斥信号量。

    ​ Process:进程。

    ​ Semaphore:计数信号量。

    ​ Thread:线程。

    ​ Waitable timer:定时器。

    ​ 原型:

    ​ DWORD WaitForSingleObject(

    ​ HANDLE hHandle, //对象句柄

    ​ DWORD dwMilliseconds //等待时间

    ​ );

    ​ 参数说明:

    ​ hHandle:等待对象的对象句柄。该对象句柄必须为SYNCHRONIZE([ˈsɪŋkrənaɪz],同步)访问。

    ​ dwMilliseconds:等待时间,单位为ms。若改值为0,函数在测试对象的状态后立即返回,若为 INFINITE(无限的),函数一直等待下去,直到收到一个信号将其唤醒,如表2-1所示。

    ​ 返回值:

    ​ 如果返回成功,其返回值说明是何种事件导致函数返回。

    函数描述:


    访问 描述

    WAIT_ABANDONED 等待对象的是一个互斥(mutex)对象,该互斥对象没有被拥有它的线程释放,他被设置为不能被唤醒

    WAIT_OBJECT_0 指定对象被唤醒

    WAIT_TIMEOUT 超时


    ​ 用法举例:

    ​ Staitic HANDLE hHandle1=NULL;

    ​ DWORD dRes;

    ​ dRes=WaitForSingleObject(hHandle1, 10); //等待对象的句柄为hHandle,等待时间1000ms

    2等待对象 (多个)

    ​ 等待多个对象WaitForMultipleObject()在指定时间内等待多个对象,他等待的对象与 WaitForSingleObject()相同。

    ​ 原型:

    ​ DWORD WaitForMultipleObject(

    ​ DWORD nCount, //句柄数组中的句柄数

    ​ XONST HANDLE *lpHandles, //指向对象句柄数组的指针

    ​ BOOL fWaitAll, //等待类型

    ​ DWORD dwMilliseconds //等待时间

    ​ );

    ​ 参数说明:

    ​ nCount:由指针*lpHandles指定的句柄数组中的句柄数,最大数是 MAXIMUM_WAIT_OBJECTS。

    ​ *lpHandles:指向对象句柄数组的指针。

    ​ fWAitAll:等待类型。若存为true,当由lpHandles数组指定的所有对象被唤醒时函数返 回;若为FALSE,当由lpHandles数组制定的某一个对象被唤醒时函数返回,且有返回值 说明事由哪个对象引起的函数返回。

    ​ dwMilliseconds :等待时间。单位为ms。若该值为0,函数测试对象的状态后立即返 回;若为INFINITE,函数一直等待下去,直到收到一个信号将其唤醒。

    ​ 返回值:

    ​ 如果成功返回,其返回值说明是何种事件导致函数返回。

    3 信号量对象

    ​ 信号量对象(semaphore)包括创建信号量(CreateSemaphore())打开信号量 OpenSemaphore()及增加信号量的值(ReleaseSemaphore())API函数。

    ​ 创建信号量

    ​ CreateSemaphore()用于创建一个信号量。

    ​ 原型:

    ​ HANDLE CreateSemaphore(

    ​ LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,//安全属性

    ​ LONG lInitialCount, //信号量对象初始值

    ​ LONG liMaximumCount, //信号量最大值

    ​ LPCTSTR lpName //信号量名

    ​ );

    ​ 参数说明:

    ​ lpSemaphoreAttributes:指定安全属性,为null是,信号量得到一个默认的安全描述符。

    ​ lInitialCount:指定信号量对象的初始值。该值必须大于等于0,小于等于lMaximumCount 。当其值大于0是,信号量被唤醒。当该函数释放了一个等待该信号量的线程时, lInitialCount值减1,当调用函数ReleaseSemaphore()时,按其指定的数量加一个值。

    ​ lMaximumCount:指出该信号量的最大值,该值必须大于0.

    ​ lpName:给出该信号量的名字。

    ​ 返回值:

    ​ 信号量创建成功,将返回该型号量的句柄。如果给出的信号量名是系统已经存在的信号量, 将返回这个已经存在的信号量的句柄。如果失败,系统返回null,还可以调用函数 GEtLastError()查询失败的原因。

    ​ 用法举例:

    ​ Static HANDLE hHandle1=null;

    ​ //创建一个信号量,其初值为0,最大值为5,信号量的名字为“SemphoreName1”

    ​ hHnadle1= CreateSemaphore(NULL,0,5,"SemphoreName1");

    							 打开信号量
    

    ​ OpenSemaphore()用于打开一个信号量。

    ​ 原型:

    ​ HANDLE OpenSemaphore(

    ​ DWORD dwDesidedAccess, //访问标志

    ​ BOOL bInheritHandle, //继承标志

    ​ LPCTSTR lpNme //信号量名

    ​ );

    ​ 参数说明:

    ​ (1).dwDesiredAccess:指出打开后要对信号量进行何种访问,如下所示。

    ​ 访问 描述

    ​ SEMAPHORE_ALL_ACCESS 可以进行任何对信号量的访问

    ​ SEMPHORE_MODFIY_STATE 可以使用ReleaseSemaphore()修改信号量的值,使信号量的 值成为可用状态

    ​ SYNCHRONIZE 使用等待函数(wait functions),等待信号量成为可用状态

    ​ (2). bInheritHandle:指出返回的的信号量句柄是否可以继承。

    ​ (3).lpName:给出信号量的名字

    ​ 返回值:

    ​ 信号量打开成功,将返回信号量的句柄;如果失败,系统返回null,可以调用函数 GetLastError()查询失败的原因。;

    ​ 用法举例:

    ​ Static HANDLE hHandle1=null;

    ​ //打开一个名为 ” SemphoreName1 ” 的信号量,之后可使用ReleaseSemaphore()函数增加 信号量的值hHandle1=OpenSemaphore(SEMAPHORE_MOFDIFY_START,NULL, ” SemphoreName1” );

    3. 增加信号量的值

    ​ ReleaseSemaphore()用于增加信号量的值。

    ​ 原型:

    ​ BOOL ReleaseSemaphore(

    ​ HANDLE hSemaphore, //信号量对象句柄

    ​ LONG lReleaseCount, //信号量要增加数值

    ​ LPLONG lpPreiousCount //信号量要增加数值地址

    ​ );

    ​ 参数说明:

    ​ (1).hSemaphore:创建或打开信号量时给出的信号量对象句柄。Windows NT中建议使用 SEMAPHORE_MODIFY_STARTE访问属性打开该信号量。

    ​ (2). lReleaseCount:信号量要增加数值。该值必须大于0。如果增加该值后大于信号创建时 给出的lMaximumCount值,则增加操作失效,函数返回FALSE。

    ​ (3). lpPreiousCount :接收信号量的一个32位的一个变量。若不需要接受该值,可以指定 为null。

    ​ 返回值:

    ​ 如果成功,将返回一个非0值;如果失败,系统返回一个0,可以调用一个GetLastError() 查询失败的原因。

    ​ 用法举例:

    ​ Static HANDLE hHandle1=NULL;

    ​ BOOL rc;

    ​ rc= ReleaseSemaphore(hHandle1,1,NULL);//给信号量的值加1;

    2.2.3实验内容

    完成主子两个线程之间的同步,要求子线程先执行。在主线程中使用系统调用 CreateThread()创建一个 子线程。主线程创建一个子线程后进入阻塞状态,直到子线程运行完毕后唤醒主线程。

    2.2.4实验要求

    ​ 能正确使用等待对象、WaitForSingleObject()或WaitForMultipleObject(0及信号量对象 CreateSemaphore()、OpenSemaphore()、ReleaseSemaphore()等系统调用,进一步理解线程的同步。

    2.2.5实验指导

    具体操作过程同本章实验一,在Microsoft visual C++6.0环境下建立一个MFC支持的控制台文件,编写C程序,在程序中使用CreateSemaphore(NULL,0,1,”SemaphoreName1”)创建一个名为“SemaphoreName1”的信号量,信号量的初始值为0,之后使用OpenSemaphore(SYNCHRONIZE|SEMAPHORE_MODIFY_STARTE,NULL, ”SemaphoreName1)打开该信号量,这里访问标志使用“SYNCHRONIZE|SEMAPHORE_MODIFY_STARTE”,以便之后可以使用WaitForSingleObject()等待该信号量及使用ReleaseSemaphore()释放该信号量,然后创建一个子线程,主线程创建子线程后调用WaitForSingleObject(hHandle1,INFINITE),这里等待时间设置为INFINITE表示一直等待下去,直到该信号量被唤醒为止。子线程结束,调用ReleaseSemaphore(hHandle1,1,NULL)释放信号量,使信号量的值加1。

    2.2.6实验总结

    实验完成了主、子线程的同步,主线程创建子线程后,主线程塞,让子线程先执行,等子线程执行完后,由子线程唤醒子线程。主子线程运行情况如图:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VWMKTzSc-1586510532837)(C:\Users\先\AppData\Roaming\Typora\typora-user-images\image-20200410170622581.png)]

    2.2.7 源程序

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

    //

    #include “stdafx.h”

    #include " Semaphore.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!\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(“WAIT_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);

    }

    展开全文
  • 一、实验目的 (1)熟悉windows系统提供的线程创建与撤销系统调用。 (2)掌握windows系统环境下线程创建与撤销方法。 (3)掌握CreateThread()函数和ExitThread()函数。 ...
  • 实验二:线程的同步 一、实验目的 (1)了解线程的历史。 (2)理解线程同步的工作原理。 (3)掌握等待一个对象函数WaitForSingleObject()。 (4)掌握等待多个对象函数WaitForMultipleObjects()。 二、实验准备 (1)....
  • (1)熟练掌握Windows系统环境下线程创建与撤销。 (2)熟悉Windows系统提供的线程互斥API。 (3)使⽤Windows系统提供的线程互斥API解决实际问题。 2.3.2 实验准备知识:相关API函数介绍 2.3.2.1临界区对象 临界...
  • 一、 实验目的 ...CeateThread()完成线程创建,它在调用进程的地址空间上创建一个线程,执行指定的函数,并返回新建立线程的句柄。 原型: HANDLE CeateThread( LPSECURITY_ATTRIBUTES lpThrea...
  • 操作系统内核级线程

    2020-05-22 16:25:36
    操作系统全部笔记目录见:操作系统笔记整理 为什么没有用户级进程?这是因为进程需要分配资源,资源是统一分配的。 多处理器,多核,如果系统没有核心级线程,多核是没什么用的。多处理器中,每个CPU都有自己的...
  • /*************************************************************************** mycmd 模仿Windows的cmd.exe (1)提供两个内部命令(不必开启子进程,在当前进程中执行), cd和dir cd c:\ ...
  • 以Linux系统进程和线程机制为背景,掌握fork()和clone()系统调用的形式和功能,以及与其相适应的高级通讯方式。由fork派生的子进程之间通过pipe通讯,由clone创建线程之间通过共享内存通讯,对于后者需要考虑互斥...
  • 以下是个人总结的实验过程及经验分享。 Tips: • 优先级 –静态优先级(nice):内核不会修改它,不随时间而变化,除非用户通过系统调用setpriority进行修改 –动态优先级(priority):内核根据线程使用CPU的...
  • 1.实验目的利用pthread_create()函数创建一个线程,在线程中更改进程中的数据 ,了解线程与进程之间的关系。2.实验软硬件环境安装Windows XP的计算机VirtualBox软件,以及在其上安装的Ubuntu虚拟机3.实验内容 在...
  • /* POSIX 下线程控制的实验程序残缺版 1.2 7 */#include &lt;stdio.h&gt; #include &lt;sys/types.h&gt;#include &lt;unistd.h&gt; #include &lt;ctype.h&gt;#include &lt;...
  • 操作系统线程

    2017-05-28 09:54:12
    一、线程的概念  线程可以理解为小型、轻型的进程,它是包含在进程中的,线程和进程的具体区别如下: 调度:一个进程可以有多个线程。...切换时的系统开销:由于在创建或者撤销进程时,系统都要为之
  • 山东大学操作系统课程设计 nachos系统实验2线程同步
  • 线程,是操作系统能够进行运算调度的最小单位。 它被包含在进程中,是进程中的实际运作单位。 线程是独立调度和分派的基本单位。 一条线程指的是进程中一个单一顺序的控制流。 一个进程中可以并发多个进程,每条线程...
1 2 3 4 5 ... 20
收藏数 45,225
精华内容 18,090