细节描述

线程基本上来说是应用程序中一条单独执行的路径。线程有时被称为轻量级进程,但线程与进程的根本不同之处在于不同进程存储空间是相互独立的,而同一进程里的所有线程共享同一地址空间。尽管这使得它更容易共享几个线程间的普通数据,但这也使得它有了另一个麻烦,即可能有多个线程同时访问一个变量,所以要小心的使用用于同步对象访问的变量,例如使用信号量(mutexes)和关键区域(critical sections)是被推荐的。另外,不要创建全局变量对象,因为在他们的构造函数里分配空间将会造成内存检查系统出现问题。

线程类型

wxWidgets有两种类型的线程:分离线程和联合线程,它们参考了POSIX 线程 API。这与win32API不同,其线程全部都是联合的。wxWidgets默认wxThreads为分离线程。分离线程一旦结束就会删除它们自己,不论是它们完成处理是自己完成删除还是通过调用Delete(),分离线程必须创建在堆上(例如通过new)。如果你想对你分配的分离线程上调用函数,你可以保存它们的实例(这句好像有问题啊,原文:Typically you'llwant to store the instances of the detached wxThreads you allocate, so that youcan call functions on them.)。因为它们的特性所致,你每次访问它们都必须使用关键区域。

//声明一个新的事件种类用于我们的MyThread类
wxDECLARE_EVENT(wxEVT_COMMAND_MYTHREAD_COMPLETED, wxThreadEvent);
wxDECLARE_EVENT(wxEVT_COMMAND_MYTHREAD_UPDATE, wxThreadEvent);wxDEFINE_EVENT(wxEVT_COMMAND_MYTHREAD_COMPLETED, wxThreadEvent);//定义事件种类
wxDEFINE_EVENT(wxEVT_COMMAND_MYTHREAD_UPDATE, wxThreadEvent);class MyFrame;//前置声明class MyThread : public wxThread
{
public:MyThread(MyFrame *handler): wxThread(wxTHREAD_DETACHED){ m_pHandler = handler }~MyThread();
protected:virtual ExitCode Entry();MyFrame *m_pHandler;
};class MyFrame : public wxFrame
{
public:...~MyFrame(){//任何对线程的清理相对于在析构函数来说最好在事件处理函数OnClose()中进行。//这是因为顶层窗口的事件循环在调用它的析构函数处不会激活,如果在窗口析构时线程发送事件,//这些事件不会被处理除非你在OnClose结束线程}...void DoStartThread();void DoPauseThread();//线程恢复和DoPauseThread()非常相似void DoResumeThread() { ... }void OnThreadUpdate(wxThreadEvent&);void OnThreadCompletion(wxThreadEvent&);void OnClose(wxCloseEvent&);
protected:MyThread *m_pThread;wxCriticalSection m_pThreadCS; // 保护m_pThread指针friend class MyThread; //友元函数,允许访问我们的线程m_pThreadwxDECLARE_EVENT_TABLE();
};wxBEGIN_EVENT_TABLE(MyFrame, wxFrame)
EVT_CLOSE(MyFrame::OnClose)
EVT_MENU(Minimal_Start, MyFrame::DoStartThread)
EVT_COMMAND(wxID_ANY, wxEVT_COMMAND_MYTHREAD_UPDATE, MyFrame::OnThreadUpdate)
EVT_COMMAND(wxID_ANY, wxEVT_COMMAND_MYTHREAD_COMPLETED, MyFrame::OnThreadCompletion)
wxEND_EVENT_TABLE()void MyFrame::DoStartThread()
{m_pThread = new MyThread(this);if ( m_pThread->Run() != wxTHREAD_NO_ERROR ){wxLogError("Can't create the thread!");delete m_pThread;m_pThread = NULL;}//在调用wxThread::Run()之后,指针m_pThread是不安全的://在任何时候线程都有可能结束(因为它完成了自己的工作)//为了避免调用无效的指针,在线程结束时OnThreadExit()中将会把指针赋值为空。
}
wxThread::ExitCode MyThread::Entry()
{while (!TestDestroy()){// ... 干一些事情...wxQueueEvent(m_pHandler, new wxThreadEvent(wxEVT_COMMAND_MYTHREAD_UPDATE));//发送事件}//通知事件句柄这个线程将会被销毁。注意:这里我们假定使用m_pHandler是安全的//(在这种情况下这将由MyFrame的析构函数确保)wxQueueEvent(m_pHandler, new wxThreadEvent(wxEVT_COMMAND_MYTHREAD_COMPLETED));//发送事件return (wxThread::ExitCode)0; // 成功
}MyThread::~MyThread()
{wxCriticalSectionLocker enter(m_pHandler->m_pThreadCS);//线程正在被销毁,确保不要调用了无效的指针m_pHandler->m_pThread = NULL;
}void MyFrame::OnThreadCompletion(wxThreadEvent&)
{wxMessageOutputDebug().Printf("MYFRAME: MyThread exited!\n");
}void MyFrame::OnThreadUpdate(wxThreadEvent&)
{wxMessageOutputDebug().Printf("MYFRAME: MyThread update...\n");
}void MyFrame::DoPauseThread()
{//任何时候我们访问m_pThread时必须先确认在此期间它不会被修改;由于一个单独线程在给出时位于一个//安全区域,所以下面的代码是安全的。wxCriticalSectionLocker enter(m_pThreadCS);if (m_pThread) // 线程仍旧存在?{//不在安全区域,一旦到达这里,下列情况可能会发生:系统调度把控制权给MyThread::Entry(),//而这时候线程可能会因为完成了处理而使指针无效if (m_pThread->Pause() != wxTHREAD_NO_ERROR )wxLogError("Can't pause the thread!");}
}void MyFrame::OnClose(wxCloseEvent&)
{{wxCriticalSectionLocker enter(m_pThreadCS);if (m_pThread) // 线程仍旧存在?{wxMessageOutputDebug().Printf("MYFRAME: deleting thread");if (m_pThread->Delete() != wxTHREAD_NO_ERROR )wxLogError("Can't delete the thread!");}}//离开安全区域来给线程进入析构函数的可能(安全区域保护m_pThreadCS)while (1){{ // 析构函数执行了吗?wxCriticalSectionLocker enter(m_pThreadCS);if (!m_pThread) break;}// wait for thread completionwxThread::This()->Sleep(1);}Destroy();
}

相反的,联合线程不会自我删除当他们完成处理,它可以安全创建在栈上。联合线程同样提供了通过Wait()来获得Entry()返回值的功能。你不需要急着把所有的线程都创建为联合线程,因为它们也拥有不利之处:你必须使用Wait()函数来给联合线程释放资源,不然它占用的系统资源不会被释放,而且你必须手动的正确的删除线程对象如果你没在栈上创建它。相反的,分离线程是“点火即忘”:你只需要开始分离线程,当它完成处理,它会自动停止并销毁它自己

线程删除

不管它是否已经停止,你都应该在联合线程调用Wait()来释放内存,就像前面线程种类里说的那样。如果你在堆上创建了一个线程,记得手动删除它们使用delete操作或类似的只有分离线程处理这种内存管理类型(后半句好奇怪,原文:If you created ajoinable thread on the heap, remember to delete it manually with thedelete operator or similar means as onlydetached threads handle this type of memory management.)

因为分离线程完成处理会自我删除,你要小心在其上调用程序。如果你确定线程正在运行,并想结束它,你可以优雅的调用来Delete()结束他(这意味着线程会在调用Delete()后删除掉)。这意味着你永远都不应该尝试使用delete或相似的操作来删除分离线程。

就像是上面提到的,Wait()Delete()都分别尝试优雅的删除分离与联合线程。它们通过等待直到线程调用TestDestroy()或结束处理(例如从wxThread::Entry返回)

(这句也有问题,原文:They do this by waiting until the thread in questioncallsTestDestroy() or endsprocessing)

明显的,如果线程已经调用TestDestroy()并且没有结束,调用Wait()Delete()的线程将会停止。这就是为什么要在你的线程的Entry()尽可能频繁的调用TestDestroy()并在它返回true时立即结束。

万不得已你可以使用Kill()立即结束线程。这种方式极度不推荐你用,因为这样并不能释放与对象相联系的资源(尽管分离线程的线程对象仍会被删除)并会是c运行库处于不安全状态。

第二线程调用绘图wxwidgets

除了“主程序线程”(运行wxApp:OnInit()或主函数运行的一个函数)之外的所有线程都被认为是“二级线程”。

GUI调用,例如对wxWindow或wxBitmap的调用,在二级线程中是不安全的,可能会过早结束你的应用程序。这是由于好几个原因:包括底层的nativeAPI,类似于其他api如MFC一样,wxThread不在二级线程运行aGUI事件循环。

工作区的一些wxWidgets端口会在任何GUI调用前调用 wxMutexGUIEnter() ,然后调用wxMutexGUILeave()。但是,推荐的方法是通过wxQueueEvent()发送的事件,在主线程中简单地处理GUI调用。但这并不代表调用这些类是线程安全的,包括 wxString在内许多类都不是线程安全的。

不要轮询wxThread

用户使用wxThread的一个普遍问题是在主线程它们会每次调用IsRunning()检查线程看他们是否已经结束,结果发现它们的应用程序出现问题了,因为线程是默认的分离线程而且线程已经自我删除。自然的,它们会尝试使用联合线程来取代前面的分离特性。然而,轮询一个来看他们是否已结束通常来说不是个好主意—实际上,如果可能,任何对正在运行的线程调用的函数都应该避免。相反的,当线程已经结束寻找一种方法来通知你自己。

通常你只需要通知主线程,在这种情况你可以通过wxQueueEvent()来发送事件给他。第二线程的情况时,如有必要,你可以调用别的类的程序当线程:完成处理、使用信号量对变量赋值、别的同步操作

测试图:

运行时

使用Delete()函数删除线程

我现在感觉联合线程应当是用于当另一线程需要等待联合线程完成某项任务时再运行,这时就可以在联合线程周期调用TestDesdory()而等待线程调用联合线程的wait(),这样就可以避免等待线程白白消耗资源。而分离线程就是自己运行完自己删除,如果需要和别的线程通信还可以用事件通知。

用于线程同步的对象

1.wxMutex--互斥量

wxMutexError  Lock ()
锁定互斥对象,相当于参数为infinite的LockTimeout() 函数。
注意:若此互斥量早已被调用线程锁定,则函数不会阻塞而会立即返回
wxMutexError  LockTimeout (unsigned long msec)
尝试锁定互斥量,在特定时间周期内
wxMutexError  TryLock ()
  尝试锁定互斥量,若不能锁定,函数会立即返回,并且返回 wxMUTEX_BUSY错误
 
wxMutexError  Unlock ()
  解锁互斥量

wxMutexLocker

wxmutex的辅助函数,构造函数中以一个互斥量为参数,他将在析构函数中解锁互斥量,这使得互斥量的解锁更加可靠,即使忘记解锁也不会造成死锁。

bool wxMutexLocker::IsOk()const

若取得互斥量所有权,返回true,否则返回false.

2.wxCriticalSection--关键区域

void  Enter ()
  进入关键区域,如果早已经有别的线程进入,则本次调用将会被阻塞,直到那个线程调用了 Leave().
注意:如果一个线程多次进入关键区域,这并不会导致死锁,事实上在这种情况下函数会立刻返回。
 
bool  TryEnter ()
  尝试进入关键区域,如果不能进入,它会立即返回false。
 
void  Leave ()
  离开关键区域使得其他线程得以访问被他保护的全局数据。

wxCriticalSectionLocker,作用类似wxMutexLocker

3.wxCondition--条件变量

wxCondError  Broadcast ()
  通知所有线程,把他们都唤醒
注意:这个函数可能会被调用,无论与之相关联的互斥量是否为锁定状态,
 
bool  IsOk () const
  若对象早已被成功初始化则返回true,若有错误发生返回false
 
wxCondError  Signal ()
  唤醒最多一个对象。
若多个线程等待同一条件变量,确切的被唤醒线程是未定义的。若无线程在等待,这次信号将会丢失,而条件变量必须再次发信号,以唤醒那些稍后可能会开始等待的线程。
注意:这个函数可能会被调用,无论与之相关联的互斥量是否为锁定状态
 
wxCondError  Wait ()
  等待直到条件变量激发。此方法将会自动解锁与条件变量相关联的互斥量的锁。然后使线程进入睡眠状态直到Signal()或Broadcast()被调用,它会再次锁定互斥量然后返回。
注意:即使Signal()在Wait()之前已经被调用,且没有唤醒任何线程,线程仍旧会继续等待下一个信号,所以确保Signal()在Wait()之后调用是很重要的,不然线程也许将一直睡眠下去
 
template<typename Functor >
wxCondError  Wait (const Functor &predicate)
  等待直到条件变量发出信号,且与之关联的条件为真。这是一个方便的重载用来忽略假的唤醒当等待特定条件变为true时。
这个函数相当于:

while ( !predicate() )

{

wxCondError e =Wait();

if ( e != wxCOND_NO_ERROR )

return e;

}

return wxCOND_NO_ERROR;

 
wxCondError  WaitTimeout (unsigned long milliseconds)
  等待直到条件被激发或超时时间到达。

4.wxSemaphore--信号量

wxSemaphore是一个计数器,用于限制并发访问共享资源的线程数。

该计数器始终在0和创建信号量期间指定的最大值之间。当计数器严格大于0时,调用wxSemaphore :: Wait()将立即返回并递减计数器。一旦达到0,任何后续的对wxSemaphore :: Wait的调用,只有当信号量计数器再次变为严格的正值时,才会返回,因为调用wxSemaphore :: Post会增加计数器的结果。

一般来说,信号量对于限制只能同时被某些固定数量的客户端访问的共享资源的访问是有用的。例如,当建模酒店预订系统时,可以创建一个具有等于可用房间总数的计数器的信号量。每次保留一个房间时,通过调用wxSemaphore :: Wait来获取信号量,并且每次释放房间时,都应该通过调用wxSemaphore :: Post来释放信号量。

wxSemaError  Post ()
  增加信号量计数值并通知等待的线程里面的一个
 
wxSemaError  TryWait ()
  与Wait()相似,但他会立即返回
 
wxSemaError  Wait ()
  无限等待直到信号量计数值变为正值,稍后会减一并返回
 
wxSemaError  WaitTimeout (unsigned long timeout_millis)
  与Wait()相似,但有超时限制

wxwidgets编写多线程程序--wxThread相关推荐

  1. 编写多线程Java应用程序常见问题

    几乎所有使用 AWT 或 Swing 编写的画图程序都需要多线程.但多线程程序会造成许多困难,刚开始编程的开发者常常会发现他们被一些问题所折磨,例如不正确的程序行为或死锁. 在本文中,我们将探讨使用多 ...

  2. [C++11 std::thread] 使用C++11 编写 Linux 多线程程序

    From: http://www.ibm.com/developerworks/cn/linux/1412_zhupx_thread/index.html 本文讲述了如何使用 C++11 编写 Lin ...

  3. Linux多线程实践(10) --使用 C++11 编写 Linux 多线程程序

    在这个多核时代,如何充分利用每个 CPU 内核是一个绕不开的话题,从需要为成千上万的用户同时提供服务的服务端应用程序,到需要同时打开十几个页面,每个页面都有几十上百个链接的 web 浏览器应用程序,从 ...

  4. 编写多线程Java应用程序

    当编写使用AWT或Swing的图形程序时,除了最琐碎的程序之外,其他所有程序都需要多线程. 线程编程带来了许多困难,许多开发人员经常发现自己容易陷入诸如错误的应用程序行为和死锁的应用程序之类的问题. ...

  5. python爬取百度贴吧中的所有邮箱_使用 Python 编写多线程爬虫抓取百度贴吧邮箱与手机号...

    原标题:使用 Python 编写多线程爬虫抓取百度贴吧邮箱与手机号 不知道大家过年都是怎么过的,反正栏主是在家睡了一天,醒来的时候登QQ发现有人找我要一份贴吧爬虫的源代码,想起之前练手的时候写过一个抓 ...

  6. volatile: 多线程程序员最好的朋友

    volatile关键字用于阻止编译器进行一些在异步事件代码中进行的可能导致错误的优化. ByAndrei Alexandrescu February 01,2001 URL:http://drdobb ...

  7. Fork and Join: Java也可以轻松地编写并发程序 原文地址 作者:Julien Ponge 译者:iDestiny 资源下载: Java SE 7 Sample Code(Zi

    Fork and Join: Java也可以轻松地编写并发程序 原文地址   作者:Julien Ponge 译者:iDestiny 资源下载: Java SE 7 Sample Code(Zip) ...

  8. volatile——多线程程序员最好的朋友

    volatile修正符及让你的编译器为你检查竞态条件(race conditions) 并不是我故意想弄糟你的心情,但是在这期专栏里,我们将讨论多线程编程这一话题.正如上一期Generic里所说的,编 ...

  9. wxWidgets:编写应用程序的快速指南

    wxWidgets:编写应用程序的快速指南 wxWidgets:编写应用程序的快速指南 wxWidgets:编写应用程序的快速指南 要设置 wxWidgets 应用程序,您需要派生一个wxApp类并覆 ...

最新文章

  1. 转:C#中的abstract与virtual
  2. MyEclipse-Web开发时何时重启tomcat、重新部署
  3. 【网页设计】框架的高度随框架里面的内容的多少而改变——转
  4. java设计模式0--设计模式简介
  5. (SpringMVC)Controller返回JSON数据
  6. [转] C# 路径(目录)
  7. .NET Core 工具中的新内容
  8. A轮股权学院:激活股权的力量,弄懂这些就够了
  9. TDD容易被忽略的五大前提
  10. H3C SecPath F100-C 防火墙默认配置
  11. Redis 最大连接数查询与设置、释放超时链接
  12. 绘制曲线设置颜色和样式
  13. 前端设计模式(1)--工厂模式
  14. 【CVRP】基于matlab节约算法求解带容量的车辆路径规划问题【含Matalb源码 157期】
  15. MyBatis-SELECT基本查询
  16. 《精通javascript》-----------------------读书笔记
  17. 字符串与Unicode编码相关转换
  18. android 定时器 误差,运动会计时器(PC+安卓)版本
  19. 《红楼梦》的香气空白
  20. python 马赛克还原_马赛克消除还原工具Depix测试

热门文章

  1. 如何在电脑上批量查询百世快运物流,并分析派件时效
  2. 28335学习之《模数转换器ADC》
  3. mysql进阶:optimize table 优化表命令 Table does not support optimize, doing recreate + analyze instead
  4. 一次性撤稿70篇!中国学者论文再现大规模撤稿 | 附全名单
  5. 基于Flask的人脸识别企业系统
  6. JNA框架调用dll动态库(给你整得明明白白)
  7. 【刷题】20230318
  8. uc/OS-II操作系统:uc/OS中的任务_下(如不懂——>请收下我的膝盖)
  9. Git_码云_账号的注册登录创建远程库
  10. 两间三层小型别墅图片_100平米两间三层楼房设计图,农村盖房舒服实用最重要...