(3)若相加结果小于等于零,则从该信号的等待队列中唤醒一个等待进程,然后再返回原进程继续执行或转入进程调度。 本次试验,我采用的线程模拟进程间的通信.
线程主要有以下几个操作
(a) 线程的启动
创建一个线程,首先要从类CwinThread产生一个派生类,同时必须使用DECLARE_DYNCREATE和IMPLEMENT_DYNCREATE来声明和实现这个CwinThread派生类。第二步是根据需要重载该派生类的一些成员函数如:ExitInstance()、InitInstance()、OnIdle()、PreTranslateMessage()等函数。最后调用AfxBeginThread()函数的一个版本:CWinThread*
AfxBeginThread( CRuntimeClass* pThreadClass, int nPriority =
THREAD_PRIORITY_NORMAL, UINT nStackSize = 0, DWORD dwCreateFlags = 0, LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL ) 启动该用户界面线程,其中第一个参数为指向定义的用户界面线程类指针变量,第二个参数为线程的优先级,第三个参数为线程所对应的堆栈大小,第四个参数为线程创建时的附加标志,缺省为正常状态,如为CREATE_SUSPENDED则线程启动后为挂起状态。
对于工作线程来说,启动一个线程,首先需要编写一个希望与应用程序的其余部分并行运行的函数如Fun1(),接着定义一个指向CwinThread对象的指针变量*pThread,调用AfxBeginThread(Fun1,param,priority)函数,返回值赋给
pThread变量的同时一并启动该线程来执行上面的Fun1()函数,其中Fun1是线程要运行的函数的名字,也既是上面所说的控制函数的名字,param是准备传送给线程函数Fun1的任意32位值,priority则是定义该线程的优先级别,它是预定义的常数,读者可参考MSDN。 (b)线程的悬挂和恢复
CWinThread类中包含了应用程序悬挂和恢复它所创建的线程的函数,其中SuspendThread()用来悬挂线程,暂停线程的执行;ResumeThread()用来恢复线程的执行。如果你对一个线程连续若干次执行SuspendThread(),则需要连续执行相应次的ResumeThread()来恢复线程的运行。 (c)线程的悬挂和恢复
4
CWinThread类中包含了应用程序悬挂和恢复它所创建的线程的函数,其中SuspendThread()用来悬挂线程,暂停线程的执行;ResumeThread()用来恢复线程的执行。如果你对一个线程连续若干次执行SuspendThread(),则需要连续执行相应次的ResumeThread()来恢复线程的运行。
(d)结束线程
终止线程有三种途径,线程可以在自身内部调用AfxEndThread()来终止自身的运行;可以在线程的外部调用BOOL TerminateThread( HANDLE hThread, DWORD dwExitCode )来强行终止一个线程的运行,然后调用CloseHandle()函数释放线程所占用的堆栈;第三种方法是改变全局变量,使线程的执行函数返回,则该线程终止。下面是第三种方式的代码:
CtestView message handlers Set to True to end thread
Bool bend=FALSE;//定义的全局变量,用于控制线程的运行;
The Thread Function;
UINT ThreadFunction(LPVOID pParam)//线程函数
{
while(!bend) {
Beep(100,100); Sleep(1000); }
return 0; }
CwinThread *pThread; HWND hWnd; Void
CtestView::OninitialUpdate()
{
hWnd=GetSafeHwnd();
pThread=AfxBeginThread(ThradFunction,hWnd);//启动线程
pThread->m_bAutoDelete=FALSE;//线程为 Cview::OnInitialUpdate();
}
Void CtestView::OnDestroy() {
bend=TRUE;//改变变量,线程结束
WaitForSingleObject(pThread->m_hThread,INFINITE);//等待线程结束
delete pThread;//删除线程 Cview::OnDestroy();
共享内存式的通信方式
通常由一个进程创建,其余进程对这块内存区进行读写。得到共享内存有两种方式:映射/dev/mem设备和内存映像文件。前一种方式不给系统带来额外的开销,但在现实中并不常用,因为它控制存取的是实际的物理内存;常用的方式是通过shmXXX函数族来实现共享内存: int shmget(key_t key, int size, int flag);/* 获得一个共享存储标识符 */ 该函数使得系统分配size大小的内存用作共享内存;
void *shmat(int shmid, void *addr, int flag);/* 将共享内存连接到自身地址空间中*/
如果一个进程通过fork创建了子进程,则子进程继承父进程的共享内存,既而可以直接对共享内存使用,不过子进程可以自身脱离共享内存。
shmid为shmget函数返回的共享存储标识符,addr和flag参数决定了以什么方式来确定连接的地址,函数的返回值即是该进程数据段所连接的实际地址。此后,进程可以对此地址进行读写操作访问共享内存。
5
进程 内存 进程 A B Hello\\0 Hello\\0 Word\\o Word\\o Hello\\0 Word\\o 对于共享内存,linux本身无法对其做同步,需要程序自己来对共享的内存做出同步计算,而这种同步很多时候就是用信号量实现。
共享内存是存在于内核级别的一种资源,在shell中可以使用ipcs命令来查看当前系统IPC中的状态,在文件系统/proc目录下有对其描述的相应文件。函数shmget可以创建或打开一块共享内存区。函数原型如下: #include
int shmget( key_t key, size_t size, int flag );
函数中参数key用来变换成一个标识符,而且每一个IPC对象与一个key相对应。当新建一个共享内存段时,size参数为要请求的内存长度(以字节为单位)。
注意:内核是以页为单位分配内存,当size参数的值不是系统内存页长的整数倍时,系统会分配给进程最小的可以满足size长的页数,但是最后一页的剩余部分内存是不可用的。
当打开一个内存段时,参数size的值为0。参数flag中的相应权限位初始化ipc_perm结构体中的mode域。同时参数flag是函数行为参数,它指定一些当函数遇到阻塞或其他情况时应做出的反应。
四、 详细设计:使用主要控件、函数;
详细设计:使用主要控件、函数;
本次试验采用三个线程线程来模拟,采用共享内存的方式。
hSemahEvent 线程2(calculate) 线程1(input) 计算两个随机数的和 获取两个随机数 缓存2 缓存1
HANDLE hEvent; //事件对象信号量
HANDLE hSemap; //线程2.3之间的信号量
线程3(print) 输出结果 6
1、线程 1 函数(获得两个随机数)
DWORD WINAPI input(LPVOID lpParameter) { }
}
return 0;
CvosDlg *Dlg = (CvosDlg *)lpParameter;
int i = 0; while( i < 100 ) {
Dlg->editVal_1.Format(_T(\),rand()0); Dlg->editVal_2.Format(_T(\),rand()0); Dlg->m_Edit_1.SetWindowTextW(Dlg->editVal_1); Dlg->m_Edit_2.SetWindowTextW(Dlg->editVal_2);
SetEvent(hEvent);//v操作
i++;
Sleep(1000);
2、线程 2 函数(计算它们的和)
DWORD WINAPI calculate(LPVOID lpParameter)
{
CvosDlg *Dlg = (CvosDlg *)lpParameter;
int i = 0;
while(i < 100) { }
7
WaitForSingleObject(hEvent,INFINITE);//p操作
int sum = _tstoi(Dlg->editVal_1) + _tstoi(Dlg->editVal_2);
Dlg->editVal_3.Format(_T(\),Dlg->editVal_1,Dlg->editVal_2,sum); Dlg->m_Edit_3.SetWindowTextW(Dlg->editVal_3); Dlg->editVal_4.Format(_T(\),sum);
Sleep(200);
ReleaseSemaphore(hSemap,1,NULL);
i++;
相关推荐: