15秒后执行一个函数,此后函数每隔5秒再执行一次, 执行三次后让函数停止

本文介绍了一个使用 JavaScript 实现的定时器示例。该示例利用 setTimeout 和 setInterval 方法来执行特定次数的任务,每5秒计数加一,共执行3次。
setTimeout(function () {
    var i = 0;
    var handler = setInterval(function () {
        i++;
        if (i == 3) {
            clearInterval(handler);
        }
    }, 5000);
}, 10000);
实验一 生产者和消费者问题 (4学时) 一、实验目的 ①掌握基本的同步互斥算法,理解生产者和消费者模型。 ②了解Windows中多线程的并发执行机制,线程间的同步和互斥。 ③学习使用Windows中基本的同步对象,掌握相应的API。 二、实验内容: 2.1创建生产者和消费者线程 在Windows环境下,创建一个控制台进程,在此进程中创建n个线程来模拟生产者或消费者。这些线程的信息由我们在本程序定义的“测试用例文件”中予以指定。该文件的格式和含义如下: 3 1 P 3 2 P 4 3 C 4 1 4 P 2 5 C 3 1 2 4 第一行说明程序中设置几个临界区,其余每行分别描述了一个生产者或者消费者线程的信息。每一行的各字段间用Tab键隔开。不管是生产者还是消费者,都有一个对应的线程号,即每一行开始字段那个整数。第二个字段用字母P或者C区分是生产者还是消费者。第三个字段表示在进入相应线程后,在进行生产和消费动作前的休眠时间,以计时;这样做的目的是可以通过调整这一列参数,控制开始进行生产和消费动作的时间。如果是代表生产者,则该行只有三个字段。如果代表消费者,则该行后面还要若干字段,代表要求消费的产品所对应的生产者的线程号。所以务必确认这些对应线程号存在并且该线程代表一个生产者。 2.2生产和消费的规则 在按照上述要求创建线程进行相应的读写操作时,还需要符合以下要求: (1)共享缓冲区存在空闲空间时,生产者即可使用共享缓冲区。 (2)从上面的“测试用例文件”例子可以看出,某一生产者生产一个产品后,可能不止一个消费者请求消费该产品,或者一个消费者多次地请求消费该产品。此时只有当所有的消费需求被满足以后,该产品所在的共享缓冲区才可以被释放,并作为空闲空间允许新的生产者使用。 (3)每个消费者线程的各个消费需求之间存在先后顺序。例如上述“测试用例文件”包含一行信息“5 C 3 1 2 4”,可知这代表一个消费者线程,该线程请求消费1,2,4号生产者线程生产的产品。而这种消费是有严格顺序的,消费1号线程产品的请求得到满足后才能继续往下请求2号生产者线程的产品。 (4)要求在每个线程发出读写操作申请、开始读写操作和结束读写操作时分别显示一行提示信息。 2.3 相关基础知识 2.3.1生产者和消费者模型 本实验所使用的生产者和消费者模型具有如下特点: (1)本实验的多个缓冲区不是环形循环的,也不要求按顺序访问。生产者可以把产品放到目前某一个空缓冲区中。 (2)消费者只消费指定生产者的产品。 (3)在“测试用例文件”中指定了所有消费者的需求,只有当共享缓冲区满足了所有关于它的消费需求后,此共享缓冲区才可以作为空闲空间允许新的生产者使用。 (4)本实验在为生产者分配缓冲区时各生产者间必须互斥,此后各个生产者的具体生产活动可以并发。而消费者之间只有在对同一产品进行消费时才需要互斥,同时它们在消费过程结束时需要判断该消费对象是否已经消费完毕并清除该产品。 2.3.2同步对象 在Windows中,常见的同步对象有:信号量(Semaphore)、互斥量(Mutex)、临界段(Critical Section)和事件(Event)等。本程序用到了前三个。使用这些对象都分为三个步骤,一是创建或者初始化;接着请求该同步对象,随即进入临界区,这一步对应于互斥量的上锁;最后释放该同步对象,这对应于互斥量的解锁。这些同步对象在一个线程中创建,在其他线程中都可以使用,从而实现同步互斥。当然,在进程间使用这些同步对象实现同步的方法是类似的。 2.4 程序的实现 2.4.1 实验环境 本实验是在VC++6.0环境下实现的,实验中所用的API(应用程序接口),是操作系统提供给应用程序调用的系统功能接口。要使用这些API,需要包括一些头文件,最常见的是windows.h。 2.4.2相关API函数的介绍 1.CreateThread 功能:创建一个在调用进程的地址空间执行的线程 格式: HANDLE CreateThread( LPSECURITY_ATTRIBUTES lpThreadAttributes, // SD SIZE_T dwStackSize, // initial stack size LPTHREAD_START_ROUTINE lpStartAddress, // thread function LPVOID lpParameter, // thread argument DWORD dwCreationFlags, // creation option LPDWORD lpThreadId // thread identifier ); 参数说明: lpThreadAttributes:指向一个SECURITY_ATTRIBUTES结构,该结构决定了返回的句柄是否可被子进程继承。若lpThreadAttributes为NULL,则句柄不能被继承。 dwStackSize:定义原始堆栈提交时的大小(按字节数计)。系统将该值舍入为最近的页数。若该值为0,或者小于默认时提交的大小,则使用默认值。 lpStartAddress:指向一个使用LPTHREAD_START_ROUTINE类型定义的函数,该线程执行函数。 lpParameter:定义一个传递给该进程的参数指针。 dwCreationFlags:定义控制线程创建的附加标志。若定义了CREATE_SUSPENDING标志,则线程创建后即处于挂起状态,并且一直到ResumeThread函数调用时才能运行。若该值为0,则该线程在创建后立即执行。 lpThreadId:线程标识符。 2.CreateMutex 功能:本API产生一个命名的或者匿名的互斥量对象。它同信号量和事件等一样可以跨线程甚至跨进程使用以实现同步。 格式: HANDLE CreateMutex( LPSECURITY_ATTRIBUTES lpMutexAttributes, // SD BOOL bInitialOwner, // initial owner LPCTSTR lpName // object name ); 参数说明: lpMutexAttributes:必须为NULL(可能将来有用)。 bInitialOwner:指明是否当前线程马上拥有该互斥量(即马上将它锁上),不必另外再进行一次加锁操作。TRUE表示锁上,FALSE表示不锁。 lpName:为互斥量指定名称。 3.CreateSemaphore 功能:本API创建一个命名的或者匿名的信号量对象。信号量可以跨线程甚至跨进程使用以实现同步。 格式: HANDLE CreateSemaphore( LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, // SD LONG lInitialCount, // initial count LONG lMaximumCount, // maximum count LPCTSTR lpName // object name ); 参数说明: lpSemaphoreAttributes:必须为NULL(可能将来有用)。 lInitialCount:信号量的初始值,取值大于等于0,小于下一个参数lMaximumCount指定的相应的最大值。 lMaximumCount:本信号量的最大值,必须大于0。 lpName:为互斥量指定名称。 4.WaitForSingleObject 功能:本API时程序处于等待状态,直到信号量hHandle出现或者超过规定的等待最长时间,信号量出现指信号量大于或等于1。本函数同样适用于互斥量。在返回之前将信号量减1或者锁上互斥锁。 格式: DWORD WaitForSingleObject( HANDLE hHandle, // handle to object DWORD dwMilliseconds // time-out interval ); 参数说明: hHandle:信号量指针(也可以是互斥) dwMilliseconds:等待的最长时间(毫)。若设置为INFINITE,则表示一直等待。 5.ReleaseSemaphore 功能:将所指信号量加上指定大小的一个量,执行成功,则返回非0值。 格式: BOOL ReleaseSemaphore( HANDLE hSemaphore, // handle to semaphore LONG lReleaseCount, // count increment amount LPLONG lpPreviousCount // previous count ); 参数说明: hSemaphore: 信号量指针。 lReleaseCount: 给目前的信号量增加的量。 lpPreviousCount:用于保存此信号量现在(未增加前的原始值),不需要时,则设置为NULL。 6.ReleaseMutex 功能:用来打开互斥锁,即将互斥量加1。成功调用则返回0。 格式: BOOL ReleaseMutex( HANDLE hMutex // handle to mutex ); 7.InitializeCriticalSection 功能:该函数初始化临界区对象。 格式: VOID InitializeCriticalSection( LPCRITICAL_SECTION lpCriticalSection // critical section ); 参数说明: lpCriticalSection:指向临界区对象的指针。 8.EnterCriticalSection 功能:该函数用于等待指定临界区对象的所有权。当调用线程被赋予所有权时,该函数返回。 格式: VOID EnterCriticalSection( LPCRITICAL_SECTION lpCriticalSection // critical section ); 参数说明: lpCriticalSection:指向临界区对象的指针。 9.LeaveCriticalSection 功能:该函数释放指定临界区对象的所有权。 格式: VOID LeaveCriticalSection( LPCRITICAL_SECTION lpCriticalSection // critical section ); 参数说明: lpCriticalSection:指向临界区对象的指针。 2.4.3程序的结构 程序分为一个函数、分别用于模拟消费者和生产者的两个函数以及三个辅助性的函数。主函数用于初始化缓冲区和各个同步对象,并完成线程信息的读入和记录,最后根据该组线程记录启动模拟线程,并等待所有线程运行结束后退出整个程序。消费者和生产者函数运行于相应线程中,完成对缓冲区的读写动作,根据此处生产消费模型的特点,生产者和消费者线程之间通过同步对象的使用实现了生产和消费动作的同步与互斥,是本实验的核心所在。另外三个辅助函数被生产者函数和消费者函数调用,是上述生产者和消费者函数中对缓冲区访问功能的一些包装。程序的流程如图1所示。 图1、程序结构图 2.4.4数据结构 (1)用一个整型数组Buffer_Critical来代表缓冲区。不管是生产产品还是对已有产品的消费都需要访问该组缓冲区。 (2)在程序中用一个自定义结构ThreadInfo记录一条线程的信息,即将“测试用例文件”中的一行信息记录下来,用于程序创建相应的生产者和消费者。由于要创建多个线程,所以程序中使用了ThreadInfo结构的数组ThreadInfo。 (3)在实现本程序的消费生产模型时,具体地通过如下同步对象实现互斥: a、设一个互斥量h_mutex,以实现生产者查询并保留缓冲区下一个空位置时进行互斥。 b、每一个生产者用一个信号量与其消费者同步,通过设置h_Semaphore[MAX_THREAD_NUM]信号量数组实现,该组信号量用于表示相应产品已生产。同时用一个表示空缓冲区数目的信号量empty_semaphore进行类似的同步,指示缓冲区中是否存在空位置,以便开始生产下一个产品。 c、每一个缓冲区用一个同步对象实现该缓冲区上消费者之间的互斥,这通过设置临界区对象数组PC_Critical[MAX_BUFFER_NUM]来实现。 2.4.5实现步骤 (1) 打开VC,选择菜单项file->new,选择projects选项卡并建立一个名为" R_WP1"的win32 console applicatoin工程;创建时注意指定创建该工程的目录; (2) 在工程中创建源文件" R_WP1.cpp":选择菜单项file->new->files->C++ Source File,在选择框中输入自己想要创建的文件名,这里是" R_WP1.cpp";在接下来询问是否创建新文件时回答"yes";然后通过Workspace->FileView->Source Files打开该文件,在其中编辑源文件并保存. (3) 通过调用菜单命令项build->build all进行编译连接,可以在指定的工程目录下得到debug-> R_WP1.exe程序,然后把给定的test.txt文件存入该debug目录下,就可以在控制台进入该debug目录运行程序了。需要强调的是在创建数据文件时,由于涉及到文件格式问题,最好在记事本中手工逐个输入数据,而不要拷贝粘贴数据。 (4)按照自己编写的测试用例文件的内容,先根据此处的消费与生产的模型分析一下应该具有什么样的执行效果,然后再实际执行程序来验证自己的分析结果。另外还可以通过改动第三列的时间参数来控制消费和生产的整个先后过程。 下面来看一个例子,测试用例文件如下: 3 1 P 5 2 P 4 3 P 2 4 C 3 1 3 2 由于我们在一个循环中创建了这四个线程,所以可以近似地认为它们是同时开始运转的。基于这样的假设,再观察第三列的时间参数,可以发现,最早动作的应该是3号生产者线程。这时它在3个缓冲区之一中生产了产品。接下来应该是四号消费者,其开始请求1号生产者的产品,由于该产品还不存在,故本线程被阻塞。接下来是2号生产者生产产品,而4号消费者依然处于阻塞状态。等待第5钟1号生产者完成生产后,4号消费者被激活,由于此时所要求的1,3,2号生产者的产品都已就绪,所以4号消费者即依次进行3个产品的消费。由于这里对每一个产品的第一次消费也是对它的最后一次消费,所以每消费一个产品后随即释放该产品所占缓冲区空间。至此,模拟程序成功运行完毕。 2.5实验任务:输出下面2个“测试用例文件”的运行结果,并加以解释。 “测试用例文件1”内容: 3 1 P 3 2 P 4 3 C 4 1 4 P 2 5 C 3 1 4 2 “测试用例文件2”内容: 2 1 P 2 2 C 1 3 1 4 3 P 4 4 P 3 三、实验要求 完成以上实验内容并写出实验报告,报告应具有以下内容: 1.实验题目。 2. 实验目的。 3.实验步骤(包括实验关键内容、程序运行过程中出现的问题及解决方法、程序运行结果,不需要程序全部源代码,可以有部分重要的源代码及注释)。 给出在Microsoft visual studio能完整的运行代码和详细步骤
最新发布
09-24
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值