精华内容
下载资源
问答
  • 使用QtConcurrent编写多线程程序

    千次阅读 2017-03-26 08:14:49
    Qt在其QtConcurrent命名空间中为我们提供了编写多线程程序的高级API,使用这个API可以使我们在不使用低级的线程元素,如互斥锁,读写锁,条件变量或者信号量的情况下编写出搞笑的多线程程序。并且,使用QtConcurrent...

    Qt在其QtConcurrent命名空间中为我们提供了编写多线程程序的高级API,使用这个API可以使我们在不使用低级的线程元素,如互斥锁,读写锁,条件变量或者信号量的情况下编写出搞笑的多线程程序。并且,使用QtConcurrent编写的程序能够自动地根据当前可以使用的处理器核心数调整实际使用的线程数目。这就意味着我们目前所写的程序即使将来的多核心机器上也能正常运行,并有很好的伸缩性。

    QtConcurrent命名空间中包括了用于并行处理的函数式编程API,其中有用于共享内存系统的MapReduce 和 FilterReduce,和用于在GUI程序中管理异步计算的相关类。其中,管理异步计算的几个类,我们在前面已经说过了,即QFuture,QFutureIterator,QFutureWatcher,QFutureSynchronizer。今天,我们主要来看一下MapReduce和FilterReduce方法。

    MapReduce相关方法包括:

    QtConcurrent::map():这个函数会将作为参数传入的函数应用到容器中的每一项,对这些项进行就地修改。

    QtConcurrent::mapped():功能类似于map(),只不过它不是在原来的容器中就地修改,而是将修改后的元素放到一个新的容器中返回。

    QtConcurrent::mappedReduce():功能类似于mapped(),只不过它会将修改过的每一项再传入另一个Reduce函数进行简化,将多个结果按某种要求简化成一个。

    FilterReduce相关方法包括:

    QtConcurrent::filter():从容器中删除那些满足某个过来条件的项。

    QtConcurrent::filtered():功能类似于filter(),只不过它会返回一个包含剩余元素的容器。

    QtConcurrent::filteredReduced():功能类似于filtered(),只不过那些剩余的元素会被进一步传入一个Reduce() 函数,进行简化。

    QtConcurrent命名空间中组合一类函数,就是我们之间讲过的run() 函数。

    下面,我们分别通过实例还看一下这些函数的使用方法。先来看Map类方法,代码如下:

    #include <QCoreApplication>
    #include <QtConcurrent>
    #include <QVector>
    #include <QDebug>
    #include <QFuture>
    
    void testMap(int& num)
    {
        num += 1;
    }
    
    int testMap2(const int& num)
    {
        return num + 1;
    }
    
    void testReduce(int& result, const int& item)
    {
        result = item > result ? item : result;
    }
    
    int main(int argc, char *argv[])
    {
        QCoreApplication a(argc, argv);
    
        QVector<int> vec;
        qsrand(QTime(0,0,0).secsTo(QTime::currentTime()));
        for(int i = 1; i <= 10; i++)
        {
            vec.push_back(qrand() % 100);
        }
        qDebug() << "origin: " << vec;
    
        //Map
        QFuture<void> f = QtConcurrent::map(vec, testMap);
        f.waitForFinished();
        qDebug() << "after map: " << vec;
        QFuture<int> r = QtConcurrent::mappedReduced(vec, testMap2, testReduce);
        qDebug() << "max: " << r.result();
    
        return a.exec();
    }
    首先,在上面的代码中,我们声明了两个map类函数。testMap()用于QtConcurrent::map(),对容器中的每一项进行就地修改;testMap2()用于QtConcurrent::MappedReduced(),将进行过map的那些元素简化成一个,在此我们是求map后容器中的最大值。注意testMap()和testMap2()的区别。

    上面的代码实现的功能很简单。先产生10个随机数存入vector,然后对该vector应用testMap()方法,将容器中的每一项加1,输出加1后的结果;然后我们再对加1处理过的容器应用testMap2()和testReduce(),其中testMap2()会对容器中的每一项再加1,然后将结果传入testReduce()方法,该方法找出当前容器中的最大值。运行结果如下:



    下面,我们再来简单看下Filter类函数,还是先上代码:

    #include <QCoreApplication>
    #include <QtConcurrent>
    #include <QVector>
    #include <QDebug>
    #include <QFuture>
    
    void testReduce(int& result, const int& item)
    {
        result = item > result ? item : result;
    }
    
    bool testFilter(const int& item)
    {
        return item >= 50;
    }
    
    int main(int argc, char *argv[])
    {
        QCoreApplication a(argc, argv);
    
        QVector<int> vec;
        qsrand(QTime(0,0,0).secsTo(QTime::currentTime()));
        for(int i = 1; i <= 10; i++)
        {
            vec.push_back(qrand() % 100);
        }
        qDebug() << "origin: " << vec;
    
        //Filter
        QFuture<void> f = QtConcurrent::filter(vec, testFilter);
        f.waitForFinished();
        qDebug() << "after filter: " << vec;
        QFuture<int> r = QtConcurrent::filteredReduced(vec, testFilter, testReduce);
        qDebug() << "max: " << r.result();
    
        return a.exec();
    }
    

    实现的功能和上面一下,将产生的随机数先进行过滤,在找出最大值。运行结果如下:



    当然,这里只是简单的演示一下该命名空间中常用函数的使用方法。大家主要要注意MapFunction、FilterFunction、ReduceFunction的声明,形式不能错。

    另外,上面演示的这些函数都是QtConcurrent命名空间中的非阻塞函数。其实,在该命名空间中还提供了一系列的阻塞函数。声明信息如下:

    void blockingFilter(Sequence &sequence, FilterFunction filterFunction)
    Sequence blockingFiltered(const Sequence &sequence, FilterFunction filterFunction)
    Sequence blockingFiltered(ConstIterator begin, ConstIterator end, FilterFunction filterFunction)
    T blockingFilteredReduced(const Sequence &sequence, FilterFunction filterFunction, ReduceFunction reduceFunction, QtConcurrent::ReduceOptions reduceOptions = UnorderedReduce | SequentialReduce)
    T blockingFilteredReduced(ConstIterator begin, ConstIterator end, FilterFunction filterFunction, ReduceFunction reduceFunction, QtConcurrent::ReduceOptions reduceOptions = UnorderedReduce | SequentialReduce)
    void blockingMap(Sequence &sequence, MapFunction function)
    void blockingMap(Iterator begin, Iterator end, MapFunction function)
    T blockingMapped(const Sequence &sequence, MapFunction function)
    T blockingMapped(ConstIterator begin, ConstIterator end, MapFunction function)
    T blockingMappedReduced(const Sequence &sequence, MapFunction mapFunction, ReduceFunction reduceFunction, QtConcurrent::ReduceOptions reduceOptions = UnorderedReduce | SequentialReduce)
    T blockingMappedReduced(ConstIterator begin, ConstIterator end, MapFunction mapFunction, ReduceFunction reduceFunction, QtConcurrent::ReduceOptions reduceOptions = UnorderedReduce | SequentialReduce)
    
    这些函数的使用方式和上面演示的非阻塞函数类似,在此就不一一演示了,大家可以自行测试,或使用上面我给出的例子进行测试。

    展开全文
  • <br /> 多线程编程可能会出现很多问题,而且有时问题无法重现,或不知什么时候会重现。或测试通过,实际运行时却出现了问题。或者在不同的虚拟机和编译器上有不同的  表现。这也是一个面试常见题,这个...

       多线程编程可能会出现很多问题,而且有时问题无法重现,或不知什么时候会重现。或测试通过,实际运行时却出现了问题。或者在不同的虚拟机和编译器上有不同的

       表现。这也是一个面试常见题,这个问题很大,有很多方面,下面谈谈我的一点体会:

        1)并不是线程越多越好。

           多线程是为了A网络服务面对同时的大量客户端请求,或同时请求大量地址如网络爬虫 

                 B,涉及GUI的图形界面编程,可以提高响应速度

                 C充分利用多核CPU的特点,提高程序执行的效率。

           一般也要十几个到几十个,具体情况具体分析

        2)正确同步访问共享的易变数据

             关于同步synchronized的两种作用A访问串行化,使可变对象在被外界观测时一直处于一种一致性的状态,否则不可访问(mutual exclusion)

                      B使一个线程对它的修改对于其他线程是可见的(communication effects)

             除long和double外的所有基本类型,或对象的引用的读写都是原子的。这保证了A,但没有保证B,所以需要volatile关键字

             (the volatile modifier performs no mutual exclusion, it

                      guarantees that any thread that reads the field will see the most recently written value)

     

                       自增运算不是原子的。the increment operator (++) is not atomic

     

                       可以共享不变对象就不会产生这样的问题。(effectively immutable,safe publication)

     

                       总结,当一些线程共享可变数据时,每个线程的读写这个数据的方法都必须显示的使用同步

                       In summary, when multiple threads share mutable data, each thread that

              reads or writes the data must perform synchronization

        3)避免过分同步

             过分同步可能会引起性能降低,死锁,甚至非确定性的行为( nondeterministic behavior.)

                      (A)To avoid liveness and safety failures, never cede control to the client within a synchronized method or block

                         换句话说,在同步区域,不要调用被设计为Override的方法,或客户端提供的 function object。

                       (B)在同步方法中要尽可能做更少的事 As a rule, you should do as little work as possible inside synchronized regions.

                          如果一个可变类是被用于多线程共享访问,你应该使它线程安全。因为内部同步比锁住整个对象的效率要高得多

                         (You should make a mutable class thread-safe (Item 70) if it is intended for

                concurrent use and you can achieve significantly higher concurrency by synchro-

                nizing internally than you could by locking the entire object externally)

               如果不是这样,就不应该使用同步。就如同StringBuffer一样,现在它被StringBuilder替代了

              In summary, to avoid deadlock and data corruption, never call an alien method

               from within a synchronized region. More generally, try to limit the amount of

               work that you do from within synchronized regions. When you are designing a

               mutable class, think about whether it should do its own synchronization. In the

               modern multicore era, it is more important than ever not to synchronize exces-

               sively. Synchronize your class internally only  if there is a good reason to do so,

               and document your decision clearly

        4)Prefer executors and tasks to threads

     

               ExecutorService executor = Executors.newSingleThreadExecutor();

                executor.execute(runnable);

                executor.shutdown();

     

             java.util.concurrent.Executors 的工厂方法提供了大多数情况下你所需要的Executor,如果你想要定制一个,那么直接使用ThreadPoolExecutor。

     

                       ScheduledThreadPoolExecutor

     

                In essence, the Executor Framework does for execution what the Collections Framework did for aggregation. 

        5)Prefer concurrency utilities to wait and notify

        6)Use lazy initialization judiciously

             7)Don’t depend on the thread scheduler

             8)Avoid thread groups

    展开全文
  • 用BCB编写多线程应用程序

    千次阅读 2009-11-05 13:30:00
    随着Windows系统的全球性普及,多线程技术已越来越多地运用到许多软件设计中。...在使用BCB以后,我才发现原来编写多线程程序也可以如此简单!BCB为我们提供了强大的TThread类,从而使得多线程编程变得非常简便易

      随着Windows系统的全球性普及,多线程技术已越来越多地运用到许多软件设计中。使用多线程技术可全面提高应用程序的执行效率。以前为了实现多线程编程,基本上都是调用一系列的API函数,如CreateThread、ResumeThread等,不容易控制,还容易出错。在使用BCB以后,我才发现原来编写多线程程序也可以如此简单!BCB为我们提供了强大的TThread类,从而使得多线程编程变得非常简便易用。下面请跟我一起开始我们的BCB多线程编程之旅。
      1. 创建多线程程序:
      首先,我介绍一下BCB中编写多线程程序的具体步骤。
      在C++Builder IDE环境下选择菜单File|New,在New栏中选中Thread Object,按OK,接下来弹出输入框,输入TThread对象子类的名字NewThread,这样C++Builder自动为你创建了一个名为TNewThread的TThread子类。下面是TNewThread的部分源码:
     __fastcall NewThread::MyThread(bool CreateSuspended)
       : TThread(CreateSuspended)
      {//构造函数,可用来初始化一些信息
      }
      void __fastcall NewThread::Execute()
      {
       //多线程程序的核心,用来执行相关多线程操作
      }

       BCB中的Execute()函数是我们要在线程中实现的任务代码所在地。使用多线程时,动态创建一个TNewThread 对象,在构造函数中使用Resume()方法,具体执行的代码使用Execute()方法重载的代码。如果想创建更多的线程,只需要创建需要数量的TNewThread 而已。
      通过以上步骤我们基本实现了在程序中如何创建一个线程,并使程序实现了多线程应用。但是,多线程应用的实现,并不是一件简单的工作,还需要考虑很多使多个线程能在系统中共存、互不影响的因素。比如,程序中公共变量的访问、资源的分配,如果处理不当,不仅线程会死锁陷入混乱,甚至可能会造成系统崩溃。总的来讲,在多线程编程中要注意共享对象和数据的处理,不能忽视。因此,下面我们要讲的就是多线程中常见问题:
      2. 多线程中VCL对象的使用
      我们都知道,C++Builder编程是建立在VCL类库的基础上的。在程序中经常需要访问VCL对象的属性和方法。不幸的是,VCL类库并不保证其中对象的属性和方法是线程访问安全的(Thread_safe),访问VCL对象的属性或调用其方法可能会访问到不被别的线程所保护的内存区域而产生错误。因此,TThread对象提供了一个Synchronize方法,当需要在线程中访问VCL对象属性或调用方法时,通过Synchronize方法来访问属性或调用方法就能避免冲突,使各个线程之间协调而不会产生意外的错误。如下所示:
      void __fastcall TNewThread::PushTheButton(void)
      
      {
       Button1->Click();
      }
      
      void __fastcall TNewThread::Execute()
      {
       ...
       Synchronize((TThreadMethod)PushTheButton);
       ...
      }
      对Button1-〉Click()方法的调用就是通过Synchronize()方法来实现的,它可以自动避免发生多线程访问冲突。在C++Builder中,虽然有一些VCL对象也是线程访问安全的(如TFont、TPen、TBrush等),可以不用Sychronize()方法对它们的属性方法进行访问调用以提高程序性能,但是,对于更多的无法确定的VCL对象,还是强烈建议使用Synchronize()方法确保程序的可靠性。
      3. 多线程中公共数据的使用
      程序设计中难免要在多个线程中共享数据或者对象。为了避免在多线程中因为同时访问了公共数据块而造成灾难性的后果,我们需要对公共数据块进行保护,直到一个线程对它的访问结束为止。这可以通过临界区域(Critical Section)的使用来实现,所幸的是在C++Builder中,给我们提供了一个TCriticalSection对象来进行临界区域的划定。该对象有两个方法,Acquire()和Release()。它设定的临界区域可以保证一次只有一个线程对该区域进行访问。如下例所示:
      class TNewThread : public TThread
      {
       ...
      private:
      TCriticalSection pLockX;
      int x;
      float y;
      ...
      };
      void __fastcall TNewThread::Execute()
      {
      ...
      pLockX->Acquire();
      x++;
      y=sin(x);
      pLockX->Release();
      ...
      }
      这样,对公共变量x,y的访问就通过全局TCriticalSection 对象保护起来,避免了多个线程同时访问的冲突。
      4. 多线程间的同步
      当程序中多个线程同时运行,难免要遇到使用同一系统资源,或者一个线程的运行要依赖另一个线程的完成等等,这样需要在线程间进行同步的问题。由于线程同时运行,无法从程序本身来决定运行的先后快慢,使得线程的同步看起来很难实现。所幸的是Windows系统是多任务操作系统,系统内核为我们提供了事件(Event)、Mutex、信号灯(semaphore)和计时器4种对象来控制线程间的同步。在C++Builder中,为我们提供了用于创建Event的TEvent 对象供我们使用。
      当程序中一个线程的运行要等待一项特定的操作的完成而不是等待一个特定的线程完成时,我们就可以很方便地用TEvent对象来实现这个目标。首先创建一个全局的TEvent对象作为所有线程可监测的标志。当一个线程完成某项特定的操作时,调用TEvent对象的SetEvent()方法,这样将设置这个标志,其他的线程可以通过监测这个标志获知操作的完成。相反,要取消这个标志,可以调用ResetEvent()方法。在需要等待操作完成的线程中使用WaitFor()方法,将一直等待这个标志被设置为止。注意WaitFor()方法的参数是等待标志设置的时间,一般用INFINITE表示无限等待事件的发生,如果其它线程运行有误,很容易使这个线程死住(等待一个永不发生的事件)。
      其实直接用Windows API函数也可以很方便地实现事件(Event)、信号(semaphore)控制技术。尤其是C++Builder,在调用Windows API方面有着其它语言无可比拟的优势。所用的函数主要有:CreateSemaphore()、CreateEvent()、WaitForSingleObject()、ReleaseSemaphore()、SetEvent()等等

    展开全文
  • wxwidgets编写多线程程序--wxThread

    千次阅读 2017-07-04 20:48:41
    尽管这使得它更容易共享几个线程间的普通数据,但这也使得它有了另一个麻烦,即可能有线程同时访问一个变量,所以要小心的使用用于同步对象访问的变量,例如使用信号量(mutexes)和关键区域(critical sect...

    细节描述

    线程基本上来说是应用程序中一条单独执行的路径。线程有时被称为轻量级进程,但线程与进程的根本不同之处在于不同进程存储空间是相互独立的,而同一进程里的所有线程共享同一地址空间。尽管这使得它更容易共享几个线程间的普通数据,但这也使得它有了另一个麻烦,即可能有多个线程同时访问一个变量,所以要小心的使用用于同步对象访问的变量,例如使用信号量(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_pThread
    	wxDECLARE_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 completion
    		wxThread::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()相似,但有超时限制

     

     

     

     

     

    展开全文
  • 如何在C#中编写多线程应用程序

    千次阅读 2008-04-07 12:24:00
    虽然也有这样那样的方法,但都不尽人意,但在C#中,要编写多线程应用程序却相当的简单。这篇文章将作简要的介绍,以起到抛砖引玉的作用! .NET将关于多线程的功能定义在System.Threading名字空间中。因此,要使用多...
  • 使用英特尔® 线程构建模块为 Windows 应用商店* 编写多线程应用”。 其中我们提到,并行计算引擎很容易导入其他移动或台式机平台。 Android 是这种移动平台的一个典型的例子。 在最新发布的英特尔® 线程构建...
  • t 甚或几 p 的数据的数据库系统,到手机上的一个有良好用户响应能力的 app,为了充分利用每个 CPU 内核,都会想到是否可以使用多线程技术。这里所说的“充分利用”包含了两个层面的意思,一
  • 编写多线程网络检测程序的简单实现 作者:秋枫 2004-7-8   前段时间编写过一个简单的网络连接检测程序,功能就是检测某一个网段中的计算机跟本机的连接情况。小程序自己用用感觉还行,不过写的比较简单,没有...
  • SQLiteDatabase 多线程访问需要注意的问题 1. 多线程打开SQLiteDatabase // Thread 1 Context context = getApplicationContext(); DatabaseHelper helper = new DatabaseHelper(context); SQLiteDatabase database ...
  • MFC多线程编程注意事项

    万次阅读 热门讨论 2008-05-26 11:17:00
    MFC多线程编程注意事项PeterLee整理 2008-05-261. 表现——错误示例关于启动线程时传输窗口对象(指针?句柄?)的问题: 在选择菜单中的开始线程后: void cmainframe::onmenu_start() { ... afxbeginthread...
  • 我们编写程序时经常犯错的几个地方: 1、以为i++是原子操作,其实不然,i++是分两步完成的,所以当我们在多个线程并发操作时就可能产生错误,例如以下代码: public class UnsafeSequence{ ...}在多线程时:
  • 描述:由C#编写多线程异步抓取网页的网络爬虫控制台程序 功能:目前只能提取网络链接,所用的两个记录文件并不需要很大。网页文本、图片、视频和html代码暂时不能抓取,请见谅。 但需要注意,网页的数目是非常...
  • VC++多线程编写经验

    千次阅读 2012-10-24 16:26:25
    十个例子清晰列举啦多线程编程的奥妙。  VC中多线程使用比较广泛而且实用,在网上看到的教程.感觉写的挺好. 一、问题的提出 编写一个耗时的单线程程序:  新建一个基于对话框的应用程序SingleThread,...
  • 一、多线程是多任务处理的一种特殊形式,多任务处理允许让电脑同时运行两个或两个以上的程序。一般情况下,两种类型的多任务处理:基于进程和基于线程。基于进程的多任务处理是程序的并发执行。基于线程的多任务处理...
  • C++ Builder 高手进阶(五)用BCB编写多线程应用程序nxyc_twz@163.com   随着Windows系统的全球性普及,多线程技术已越来越多地运用到许多软件设计中。使用多线程技术可全面提高应用程序的执行效率。以前为了实现...
  • 编写多线程程序,使用了srand和rand函数,在类的构造函数加入了srand,在某个功能函数使用rand,运行发现每次都生成同样的随机数。经过查文档才发现,srand是线程相关的和对象没有关系。对象定义在主线程里,而功能...
  • Java多线程学习(吐血超详细总结)

    万次阅读 多人点赞 2015-03-14 13:13:17
    本文主要讲了java中多线程的使用方法、线程同步、线程数据传递、线程状态及相应的一些线程函数用法、概述等。
  • 学习QT多线程编程之前,有必要先熟悉事件循环的概念。先看一个单线程界面程序的主函数代码: int main(int argc, char* argv[]) {  QApplication app(argc, argv);  // 构造主窗口对象并显示  MainWindow w;  ...
  • Java多线程超详解

    万次阅读 多人点赞 2019-06-11 01:00:30
    随着计算机的配置越来越高,我们需要将进程进一步优化,细分为线程,充分提高图形化界面的多线程的开发。这就要求对线程的掌握很彻底。 那么话不多说,今天本帅将记录自己线程的学习。 线程的相关API //获取当前...
  • VB.NET 2010编写多线程的经验和注意事项。
  • 采取多线程编写的CSocket网络聊天程序,包括客户端和服务器端,在VS2008下编写,在VS2005下是无法运行的,所以请注意
  • Java编写多线程端口扫描器

    万次阅读 2012-12-05 18:25:00
    如果创建失败(端口已经被占用,即系统已经创建了此端口的监听对象实体),因此可以用一个ServerSocket对象逐个创建探测,考虑到端口太多,一个线程执行太慢,所以利用了多线程,整个程序运行时间大约为5-10秒。...
  • C++11多线程注意事项以及detach中的坑

    千次阅读 2019-05-02 15:11:43
    以前多线程基本是靠系统API或者第三方库完成的,比如windows的API函数CreateThread,linux创建线程函数pthread_create,但是这样编写的代码不可移植,不能跨平台,比如windows的多线程程序拿到linux下是跑不起来的,...
  • Java编写多线程本机端口扫描器

    千次阅读 2016-02-02 10:33:37
    如果创建失败(端口已经被占用,即系统已经创建了此端口的监听对象实体),因此可以用一个ServerSocket对象逐个创建探测,考虑到端口太多,一个线程执行太慢,所以利用了多线程,整个程序运行时间大约为5-10秒。...
  • 在上一篇博文中,我们介绍了利用socket进行简单的UDP/TCP的服务端和客户端的通信。...答案是使用多线程,一个线程用于接受数据,另一个线程用来发送数据。接下来我们介绍WinSock的多线程编程。 多线程
  • 点击进入_更多_Java千百问1、如何编写单线程Socket程序了解Socket看这里:Socket是什么 ...了解多线程Socket看这里:如何编写多线程Socket程序 要编写Socket,需要了解java.net包中提供了两个类Sock
  • Java多线程详解

    千次阅读 多人点赞 2019-07-29 17:18:03
    今天我们聊一聊多线程,谈到多线程,很多人就开始难受,这是一个一听就头疼的话题,但是,我希望你在看完这篇文章后能对多线程有一个深入的了解。 案例 那么,首先我就举一个电影院卖票的例子来模拟多线程。 复仇者...
  • 编写mysql客户端程序时,最常见的就是连接mysql,和关闭mysql,在这里需要注意,如果使用不当将会造成内存泄漏。 单线程环境:一般使用 -lmysqlclient链接mysql客户库,mysql_init函数会自动调用my_library_...
  • JAVA多线程使用场景和注意事项

    千次阅读 2019-03-15 17:59:58
    我曾经对自己的小弟说,如果你实在搞不清楚什么时候用HashMap,什么时候用...多线程生来就是复杂的,也是容易出错的。一些难以理解的概念,要规避。本文不讲基础知识,因为你手里就有jdk的源码。 线程 Threa...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 260,884
精华内容 104,353
关键字:

编写多线程需要注意什么