b2科目四模拟试题多少题驾考考爆了怎么补救
b2科目四模拟试题多少题 驾考考爆了怎么补救

理解多线程管理类 CWorkQueue

电脑杂谈  发布时间:2019-06-19 23:10:38  来源:网络整理

c threadpool 线程池_c 多线程 多任务_c 线程

①线程在执行同步代码块或同步方法时,程序调用thread.sleep()方法,thread.yield()方法来暂停当前线程的执行,当前线程不会释放同步监视器的锁定。第4章 线程间通信概述 4.1 线程之间通信的方法 4.1.1 全局变量方式 4.1.2 参数传递法 4.1.3 消息传递法 4.1.4 通过同步变量进行线程间通信 4.2 线程间同步问题概述 4.3 死锁问题 4.4 本章小结。(一般情况下在执行同步代码块时不会释放同步锁,但也有特殊情况会释放对象锁如在执行同步代码块时,遇到异常而导致线程终止,锁会被释放。

以前做项目时,用过 Codeproject 上一个线程管理的代码 Work Queue[1],很好用,也是不错的学习资料,但对于多线程初学者也不是一眼就能看懂的,所以今天打算对这个代码做个解读笔记,可为其它学习者提供一个参考,也深化自己对多线程的理解。关于该类的使用可直接访问原网址。

这个多线程管理类为CWorkQueue,使用的是生产者-消费者模式。CWorkQueue 创建的每个线程都是一个消费者,生产者是类成员m_pWorkItemQueue。生产者资源由外界使用者通过InsertWorkItem 成员函数注入,然后通过ReleaseSemaphore 通知消费者(即线程)处理,消费者线程ThreadFunc 自创建起始就一直在等待,等待生产者通知,接到有任务通知后,线程就执行任务,执行完毕后继续等待。

主线程通过两种机制来跟已建立的新线程通信,信号量Semaphore 和 事件 Eventc 多线程 多任务,Semaphore 用于通知新线程执行任务,Event 用于通知新线程结果自己。

1、信号量Semaphore

由上可知,生产者和消费者使用的通信机制是信号量Semaphore。信号量Semaphore 只有两种状态,触发和未触发,决定状态的是当前资源数量,数量大于0表示信号量处于触发,等于0表示资源已经耗尽故信号量处于末触发。影响当前资源数量的函数有几个,下面依次介绍。

c 线程_c 多线程 多任务_c threadpool 线程池

其实,不但创建队列实际调用通用队列创建函数,二进制信号量、计数信号量、互斥量和递归互斥量也都直接或间接使用这个函数,如表1-1所示。前面第21章,我们对计数信号量进行了讲解,计数信号量就是对一个变量进行计数,变量的范围是从0到用户创建计数信号量时所设置的大小。创建互斥量和创建信号量的api函数不同,但是共用获取和给出信号api函数。

m_phSincObjectsArray[SEMAPHORE_INDEX] = CreateSemaphore(NULL,0,LONG_MAX,NULL); //创建Semaphore对象

一般是将当前可用资源计数设置为最大资源计数 , 每增加一个线程对共享资源的访问 , 当前可用资源计数就会减 1 , 只要当前可用资源计数是大于 0 的 , 就可以发出信号量信号 。每当线程调用waitforsingleobject函数并传入一个信号量对象的句柄,系统将检查该信号量的资源计数是否大于0,如果大于0,系统就将资源计数减去1,并唤醒线程。其中,二进制信号量、计数信号量、互斥量和递归互斥量都是使用队列来实现的,因此掌握队列的运行机制,是很有必要的。

if (!ReleaseSemaphore(m_phSincObjectsArray[SEMAPHORE_INDEX],1,NULL)) 
{
  assert(false);
  return false;
}

此函数检查指定的对象或事件的状态,如果该对象处于无信号状态,则调用线程处于等待状态,此时该线程不消耗cpu时间,。有很多封装的线程库中有这样的函数 condwait函数,这个函数的主要意义就是释放互斥量,等待信号量,然后再持有互斥量后返回,主要用于多线程运行状态下,等待一个信号通知,然后继续运行。在对信号量调用等待函数时,等待函数会检查信号量的当前资源计数,如果大于0(即信号量处于触发状态),减1后返回让调用线程继续执行。

//等待两个事件
dwWaitResult = WaitForMultipleObjects(NUMBER_OF_SYNC_OBJ,pWorkQueue->m_phSincObjectsArray,FALSE,INFINITE);

c threadpool 线程池_c 多线程 多任务_c 线程

2、事件 Event

该函数创建线程,程结束的时候会开启新的任务,将事件添加进epoll关注,epoll_wait返回后就注销事件,并创建新的线程去处理这个事件。注意代码中的ostasksempend()函数就是和开头的信号发布相对应,当用户创建一个定时器(注意:在进行创建定时器时一定要先定义一个定时器变量一般都是全局的)osmrcreate()函数是用来创建一个定时器,创建完成后还需要在调用ostmrstart()来启动定时器,时内部调用的ostmrlink()函数将按照定时器剩余时间采用哈希算法将其加入定时器列表,其中ostmrcreate主要有这样几个变量需要注意一下。c、当其他线程调用pthread_cond_signal或pthread_cond_broadcast时,会唤醒相应条件变量等待的线程,此时被唤醒的线程,可以参与调度了,此时被唤醒的线程继续执行pthread_cond_wait()函数,函数pthread_cond_wait()返回之前,会重新给条件变量对应的互斥量上锁,在这里就是nready.mutex,若该函数成功返回,则当前线程有重新获得了nready.mutex锁,当然nready.mutex也可能被其他线程继续占有c 多线程 多任务,此时线程再次阻塞。

m_phSincObjectsArray[ABORT_EVENT_INDEX] = CreateEvent(NULL,TRUE,FALSE,NULL); //创建event 事件对象,初始化时为无信号状态,使用手动重置为无信号状态

该函数创建线程,程结束的时候会开启新的任务,将事件添加进epoll关注,epoll_wait返回后就注销事件,并创建新的线程去处理这个事件。调用时输入23和24.5,输入的这2个参数才是真正需要传递给函数的参数,ref int, ref double是告诉alien需要分配空间,调用c函数从栈中获取它的参数,调用结束后将返回结果放到栈中(为了区分返回结果和栈中的其他的值,每个c函数还会返回结果的个数),然后lua函数返回结果值。先说说用getmodulehandle函数获取.此方法需要用createremotethread函数在远程进程中创建线程,让该线程调用kernel32.dll中的getmodulehandle函数.但是只这样还不行.因为getmodulehandle函数需要以dll模块文件名做参数.既然是远程线程调用getmodulehandle函数.还需要先把dll模块文件名写入目标进程的地址空间中.最后用createremotethread函数创建线程执行getmodulehandle函数来获取dll句柄。

因为信号量和事件都是HANDLE,所以该类中把这两个存在一个数组m_phSincObjectsArray,通过枚举型变量来标识。

3、生产者m_pWorkItemQueue

c 多线程 多任务_c 线程_c threadpool 线程池

用户通过InsertWorkItem 插入工作任务到队列。

bool CWorkQueue::InsertWorkItem(WorkItemBase* pWorkItem)

工作任务由要线程执行的函数,和 Abort 函数,Abort 函数用于最后调用 Destroy 突然终止线程时,执行剩余线程的清理工作。

class WorkItemBase
{
   virtual void   DoWork(void* pThreadContext)    = 0;
   virtual void   Abort () = 0;
   friend CWorkQueue;
};

WorkItemBase 需要用户来继承,并实现两个虚函数。

class SpecificWorkItem : public WorkItemBase
{
    void   DoWork(void* pThreadContext);
    void   Abort();
 //memeber variables needed here
};
void   SpecificWorkItem::DoWork(void* pThreadContext)
{
  //Notify Start
  //proccessing done here
  //Notify Finish
  //free all that was occupied
}
void   SpecificWorkItem::Abort()
{
  //Notify aborted
  //free all that was occupied
}

c 线程_c threadpool 线程池_c 多线程 多任务

这样在用户执行InsertWorkItem 插入任务到队列后,通过ReleaseSemaphore 激发信号量Semaphore,线程ThreadFunc 会在 WaitForMultipleObjects 中探测到Semaphore 的激发状态,然后获得队列中的任务,并删除队列中该任务防止其它线程重复执行,然后执行用户的任务pWorkItem->DoWork(pThreadData),此处pThreadData 我没用到,为 NULL 。

4、消费者ThreadFunc

消费者线程 ThreadFunc在Create 中创建,返回程句柄存储在类成员数组m_phThreads 中,供 Destroy 函数等待线程结束时使用。

DWORD dwThreadId;
PTHREAD_CONTEXT pThreadsContext ;
//创建所有的线程
for(i = 0 ; i < nNumberOfThreads ; i++ )
{  
   //初始化每个线程的上下文,用于传递给线程函数
  pThreadsContext = new THREAD_CONTEXT;
  pThreadsContext->pWorkQueue  = this; //传递当前对象的指针,使新建线程可以访问到主线程中的生产者资源
  pThreadsContext->pThreadData = ThreadData == NULL? NULL : ThreadData[i];       
  //创建线程
  m_phThreads[i] = CreateThread(NULL,
      0,
      CWorkQueue::ThreadFunc,
      pThreadsContext,
      0, //为0表示线程创建之后立即就可以进行调度
      &dwThreadId);
}

5、由消费者线程去访问生产者资源可以看出,同一个进程的线程可共享内存。用户可以通过SpecificWorkItem 构造函数把数据和函数传进成员变量和成员函数,通过InsertWorkItem 把SpecificWorkItem 传进CWorkQueue 的m_pWorkItemQueue,即生产者任务队列,然后消费者线程会获得通知,并访问生产者提取任务,执行函数和数据。

参考资料:

1、


本文来自电脑杂谈,转载请注明本文网址:
http://www.pc-fly.com/a/jisuanjixue/article-107162-1.html

    相关阅读
      发表评论  请自觉遵守互联网相关的政策法规,严禁发布、暴力、反动的言论

      • 廖国
        廖国

        是因为你真的真的值得爱

      每日福利
      热点图片
      拼命载入中...