【C++】POCO学习总结(六):线程、线程池、同步

【C++】POCO学习总结(六):线程、线程池、同步C 郭老二博文之 C 目录 1 线程 1 1 所属库 头文件 Poco 中线程类是 Poco Thread 在基础库 Foundation 中 对应动态库 libPocoFound so 使用时 需要包含头文件 include

大家好,我是讯享网,很高兴认识大家。

【C++】郭老二博文之:C++目录

1、线程

1.1 所属库、头文件

1.2 属性

1.2.1 名字和ID

可以给每个线程起一个名字(通过构造函数、或者setName());
每个线程有一个唯一的ID
相关函数:getName()、setName()、id()

1.2.2 优先级

可以给每个线程指定一个优先级,POCO定义了五级优先级:

  • PRIO_LOWEST —— 最低线程优先级.
  • PRIO_LOW —— 低于正常线程优先级.
  • PRIO_NORMAL —— 正常线程优先级.
  • PRIO_HIGH —— 高于正常线程优先级.
  • PRIO_HIGHEST—— 最高线程优先级.

注意:有些平台需要特殊权限(root)来设置或更改线程的优先级。
如果POCO提供的五级优先级不够用,可以使用setOSPriority()来设置特定操作系统的线程优先级;
可以使用getMinOSPriority()和getMaxOSPriority()来找出优先级值有效的范围;

1.2.3 线程堆栈大小

线程的堆栈大小可以通过setStackSize(int size)来设置;
如果size为0,则使用操作系统默认的堆栈大小;
getStackSize()函数返回指定线程的堆栈大小;

1.2.4 其它常用函数

isRunning():检查线程是否正在运行;
current():返回当前线程的Thread对象指针,注意:主线程没有thread对象,因此返回空指针;
Thread::sleep():暂定
Thread::yield():将当前线程所抢到的CPU”时间片”让给其他线程

1.3 Poco::Runnable

1.3.1 用法

对Qt熟悉的Qter们,应该对QThread的用法比较熟悉:

  • 子类化QThread
  • 重写(override)虚函数run()
  • 调用start(),来启动线程

巧了,Poco::Runnable的用法和QThread很相似。
官方的介绍是:Poco::Runnable是一个接口类(需要重写run()),实现了线程入口功能。
使用时,需要添加头文件:#include “Poco/Runnable.h”

1.3.2 示例

#include "Poco/Thread.h" #include "Poco/Runnable.h" #include <iostream> class HelloRunnable: public Poco::Runnable { 
    virtual void run() { 
    std::cout << "Hello, world!" << std::endl; } }; int main(int argc, char** argv) { 
    HelloRunnable runnable; Poco::Thread thread; thread.start(runnable); thread.join(); return 0; } 

讯享网

1.4 Poco::RunnableAdapter

1.4.1 用法

讯享网Poco::RunnableAdapter(C& object, Callback method): _pObject(&object), _method(method) void run() { (_pObject->*_method)(); } 

1.4.2 示例

#include "Poco/Thread.h" #include "Poco/RunnableAdapter.h" #include <iostream> class Laoer { 
    public: void say() { 
    std::cout << "Hello, world!" << std::endl; } }; int main(int argc, char** argv) { 
    Laoer laoer; Poco::RunnableAdapter<Laoer> runnable(laoer, &Laoer::say); Poco::Thread thread; thread.start(runnable); thread.join(); return 0; } 

2、线程池

2.1 使用线程池的好处

1)节省开销:创建一个新线程需要一些时间,重用线程可以节省重复创建的时间和资源。
2)管理简单:不用分神去管理线程对象的生命周期
3)控制线程数:可以控制线程数量

2.2 用法

POCO中线程池类是Poco::ThreadPool,头文件:#include “Poco/ThreadPool.h” ;
线程池有一个最大容量,如果容量耗尽,则在请求新线程时抛出异常:Poco::NoThreadAvailableException。
线程池容量可以动态增加:void addCapacity(int n)
POCO提供了一个默认的ThreadPool实例,初始容量为16个线程。

当线程池中的线程空闲一定时间时,会被自动收集;也可以通过调用collect()来强制收集。

2.3 示例

讯享网#include "Poco/ThreadPool.h" #include "Poco/Runnable.h" #include <iostream> class HelloRunnable: public Poco::Runnable { 
    virtual void run() { 
    std::cout << "Hello, world!" << std::endl; } }; int main(int argc, char** argv) { 
    HelloRunnable runnable; Poco::ThreadPool::defaultPool().start(runnable); Poco::ThreadPool::defaultPool().joinAll(); return 0; } 

3、线程局部变量

3.1 说明

Poco::ThreadLocal是线程局部变量,也叫线程局部存储,意思是模版ThreadLocal中填充的变量属于当前线程,该变量对其他线程而言是隔离的,也就是说该变量是当前线程独有的变量。

3.2 示例

#include "Poco/Thread.h" #include "Poco/Runnable.h" #include "Poco/ThreadLocal.h" #include <iostream> class Counter: public Poco::Runnable { 
    void run() { 
    static Poco::ThreadLocal<int> tls; for (*tls = 0; *tls < 10; ++(*tls)) { 
    std::cout << Poco::Thread::current()->id() << ":" << *tls << std::endl; } } }; int main(int argc, char** argv) { 
    Counter counter; Poco::Thread t1; Poco::Thread t2; t1.start(counter); t2.start(counter); t1.join(); t2.join(); return 0; } 

编译:

讯享网g++ thread.cpp -I ~/git/poco/install/include -L ~/git/poco/install/lib -lPocoFoundationd -lpthread 

输出:

1:0 1:1 …… 1:8 1:9 2:0 2:1 …… 2:8 2:9 

4、线程错误处理

4.1 说明

4.2 示例

$ vi threadErrHeadle.cpp

讯享网#include "Poco/Thread.h" #include "Poco/Runnable.h" #include "Poco/ErrorHandler.h" #include <iostream> using namespace Poco; class Offender: public Poco::Runnable { 
    void run() { 
    throw Poco::ApplicationException("got you"); } }; class MyErrorHandler: public Poco::ErrorHandler { 
    public: void exception(const Poco::Exception& exc) { 
    std::cerr << exc.displayText() << std::endl; } void exception(const std::exception& exc) { 
    std::cerr << exc.what() << std::endl; } void exception() { 
    std::cerr << "unknown exception" << std::endl; } }; int main(int argc, char** argv) { 
    MyErrorHandler eh; ErrorHandler* pOldEH = Poco::ErrorHandler::set(&eh); Offender offender; Thread thread; thread.start(offender); thread.join(); Poco::ErrorHandler::set(pOldEH); return 0; } 

编译:

g++ threadErrHeadle.cpp -I ~/git/poco/install/include -L ~/git/poco/install/lib -lPocoFoundationd -lpthread 

5、线程同步

5.1 Poco::Mutex 递归互斥锁

5.1.1 说明

Poco::Mutex是递归(recursive)互斥锁:同一个互斥锁可以被同一个线程多次锁定(但不能被其他线程锁定)

相关函数:
1)lock():获取互斥锁,如果互斥锁被其他线程持有,则等待;
2)lock(long millisecs):获取互斥锁,如果互斥锁由另一个线程持有,则阻塞到给定的毫秒数。超时则抛出TimeoutException;
3)unlock():释放互斥锁,使它可以被另一个线程获取;
4)tryLock():尝试获取互斥锁。如果互斥锁由另一个线程持有,则立即返回false;如果互斥锁已被获取,则立即返回true。
5)tryLock(long millisecs):尝试在给定的时间段内获取互斥锁。如果获取锁失败则返回false,如果已获取互斥锁则返回true。

5.1.2 Poco::Mutex::ScopedLock

作用域锁:即在利用作用域,在Poco::Mutex::ScopedLock构造函数中加锁,在析构函数中解锁

5.1.3 示例

讯享网#include "Poco/Mutex.h" using Poco::Mutex; class Concurrent { 
    public: void criticalSection() { 
    Mutex::ScopedLock lock(_mutex); // ... } private: Mutex _mutex; }; 

5.2 Poco::FastMutex 非递归互斥锁

Poco::FastMutex是非递归(non-recursive)互斥锁:同一个互斥锁,被再次锁定时,将导致死锁

API和使用方法同Poco::Mutex 。

5.3 Poco::Event 事件

5.3.1 说明

Poco::Event 是一个同步对象,它允许一个线程向一个或多个其他线程发出某个事件发生的信号。和信号量相似
Poco::Event 有两种形式:

  • auto reset:在唤醒最多一个等待线程后,事件将失去其信号状态
  • manual reset:事件将保持信号状态,直到被手动复位

5.3.2 用法

  • 对于自动重置(默认),将true传递给构造函数。
  • 对于手动重置,将false传递给构造函数。

1)set():发出事件信号。如果事件是自动重置的,则最多有一个等待事件的线程被唤醒,信令状态被重置。否则,所有等待事件的线程都会被唤醒。
2)wait()和wait(long milliseconds):等待事件成为信号。如果给出了超时时间,并且在给定的时间间隔内没有发出事件信号,则抛出TimeOutException
3)tryWait(long milliseconds):等待事件成为信号。如果事件在给定的间隔内发出信号,则返回true。否则,返回false
4)reset():重置(手动重置)事件

5.4 Poco::Condition 条件变量

5.4.1 说明

Poco::Condition 用于阻塞线程,直到满足特定条件。
Poco::Condition 总是与Mutex(或FastMutex)一起使用。
Poco::Condition 类似于POSIX条件变量,区别在于条件不受虚假唤醒的影响。

等待条件的线程按FIFO顺序恢复

5.4.2 用法

头文件:#include "Poco/Condition.h
Poco::Condition是基于模板的,适用于任何类型的互斥对象。实现基于Poco::Event和用于所有平台上等待线程的std::deque
1)等待wait():

template <class Mtx> void wait(Mtx& mutex) template <class Mtx> void wait(Mtx& mutex, long milliseconds) 

解锁互斥锁(在调用wait()时必须被锁定)并等待(给定的时间)直到Condition发出信号。
给定的互斥锁将在成功离开函数后再次被锁定,即使在出现异常的情况下也是如此。
如果在给定的时间间隔内没有发出条件信号,则抛出TimeoutException。

2)尝试等待tryWait:不会抛异常


讯享网

讯享网template <class Mtx> bool tryWait(Mtx& mutex, long millisecs) 

5.5 Poco::Semaphore 信号量

5.5.1 说明

5.5.2 用法

头文件:#include “Poco/Semaphore.h”

5.6 Poco::RWLock 读写锁

5.6.1 说明

Poco::RWLock允许多个并发读,或一个独占写。

5.6.1 用法

头文件:#include "Poco/RWLock.h
常用函数:
1)void readLock():获取读锁。如果另一个线程持有写锁,则等待直到写锁被释放
2)bool tryReadLock():获取读锁。如果另一个线程持有写锁,则立即返回false
3)void writeLock():获取写锁。如果其他线程当前持有锁,则等待直到所有锁都被释放
4)bool tryWriteLock():获取写锁。如果其他线程当前持有锁,则立即返回false
5)void unlock():解锁

6、线程高级用法

6.1 Poco::Timer 顺序定时器

6.1.1 说明

计时器启动一个线程(从线程池中获取),该线程首先等待给定的启动间隔。
一旦开始时间间隔过期,计时器将调用回调函数。
回调函数返回后,且周期间隔不为零,计时器重复等待周期间隔,然后调用回调函数。

6.1.2 用法

  • 定时器可以通过设置周期间隔为零来停止。
  • 计时器回调函数在计时器的线程中运行,因此可能需要同步。
  • 定时器的精度取决于许多因素,如操作系统,系统负载等,因此实时性较差。

6.1.3 示例

$ vi timer.cpp

#include "Poco/Timer.h" #include "Poco/Thread.h" #include <iostream> using Poco::Timer; using Poco::TimerCallback; class TimerExample { 
    public: void onTimer(Poco::Timer& timer) { 
    std::cout << "onTimer called." << std::endl; } }; int main(int argc, char** argv) { 
    TimerExample te; Timer timer(250, 500); // 250ms后启动,每500ms重复一次 timer.start(TimerCallback<TimerExample>(te, &TimerExample::onTimer)); Poco::Thread::sleep(5000); timer.stop(); return 0; } 

编译

讯享网g++ timer.cpp -I ~/git/poco/install/include -L ~/git/poco/install/lib -lPocoFoundationd -lpthread 

输出

$ ./a.out onTimer called. onTimer called. onTimer called. …… 

6.2 Poco::TaskManager 任务管理

6.2.1 说明

例如:当需要在GUI(或服务器)应用程序中跟踪一个或多个后台处理线程的进度,可以使用Poco::Task和Poco::TaskManager

6.2.2 用法

1)头文件
#include “Poco/Task.h”
#include “Poco/TaskManager.h”

  • Poco::TaskStartedNotification
  • Poco::TaskCancelledNotification
  • Poco::TaskFinishedNotification
  • Poco::TaskFailedNotification
  • Poco::TaskProgressNotification
  • Poco::TaskCustomNotification

6.2.3 示例

$ vi task.cpp

讯享网#include "Poco/Task.h" #include "Poco/TaskManager.h" #include "Poco/TaskNotification.h" #include "Poco/Observer.h" #include <iostream> using Poco::Observer; class SampleTask: public Poco::Task { 
    public: SampleTask(const std::string& name): Task(name) { 
   } void runTask() { 
    for (int i = 0; i < 100; ++i) { 
    setProgress(float(i)/100); // report progress if (sleep(10)) break; } } }; class ProgressHandler { 
    public: void onProgress(Poco::TaskProgressNotification* pNf) { 
    std::cout << pNf->task()->name() << " progress: " << pNf->progress() << std::endl; pNf->release(); } void onFinished(Poco::TaskFinishedNotification* pNf) { 
    std::cout << pNf->task()->name() << " finished." << std::endl; pNf->release(); } }; int main(int argc, char** argv) { 
    Poco::TaskManager tm; ProgressHandler pm; tm.addObserver( Poco::Observer<ProgressHandler, Poco::TaskProgressNotification> (pm, &ProgressHandler::onProgress) ); tm.addObserver( Poco::Observer<ProgressHandler, Poco::TaskFinishedNotification> (pm, &ProgressHandler::onFinished) ); tm.start(new SampleTask("Task 1")); // tm takes ownership tm.start(new SampleTask("Task 2")); tm.joinAll(); return 0; } 

编译

$ g++ task.cpp -I ~/git/poco/install/include -L ~/git/poco/install/lib -lPocoFoundationd -lpthread -std=c++11 

输出

讯享网$ ./a.out Task 1 progress: 0.1 Task 2 progress: 0.19 …… Task 2 progress: 0.83 Task 1 progress: 0.92 Task 1 finished. Task 2 finished. 

6.3 Poco::Activity 活动对象

6.3.1 什么是活动对象?

  • Activity是一个可能长时间运行的void/no参数成员函数,运行在自己的线程中。
  • ActiveMethod是一个非空的单参数成员函数,在自己的线程中运行。
  • 所有活动方法可以共享一个线程(在这种情况下,调用将被排队),或者每个方法都有自己的线程。
  • 活动可以在对象构造时自动启动,也可以在稍后的时间手动启动。
  • 活动可随时停止。为此,活动必须定期调用isStopped()成员函数。
  • 实现活动的方法不能有参数或返回值。
  • 活动的线程从默认线程池中获取

6.3.2 示例

$ vi active.cpp

#include "Poco/Activity.h" #include "Poco/Thread.h" #include <iostream> using Poco::Thread; class ActivityExample { 
    public: ActivityExample(): _activity(this, &ActivityExample::runActivity) { 
   } void start() { 
    _activity.start(); } void stop() { 
    _activity.stop(); // request stop _activity.wait(); // wait until activity actually stops } protected: void runActivity() { 
    while (!_activity.isStopped()) { 
    std::cout << "Activity running." << std::endl; Thread::sleep(200); } } private: Poco::Activity<ActivityExample> _activity; }; int main(int argc, char** argv) { 
    ActivityExample example; example.start(); Thread::sleep(2000); example.stop(); return 0; } 

编译:

讯享网$ g++ active.cpp -I ~/git/poco/install/include -L ~/git/poco/install/lib -lPocoFoundationd -lpthread -std=c++11 

输出:

~/test/poco$ ./a.out Activity running. …… Activity running. 

6.4 Poco::ActiveMethod 活动方法

6.4.1 什么是活动方法?

活动方法是在自己的线程(取自默认线程池)中执行的成员函数。
活动方法可以共享一个线程。在这种情况下,当其他活动方法在队列中等待执行时,一次只能执行一个活动方法。
活动方法只接受一个参数并返回一个值。
要传递多个参数,可以使用struct、std::pair或Poco::Tuple。
活动方法的返回值保存在Poco::ActiveResult(也称为Future)中。

6.4.2 用法

由于活动方法的返回值在调用该方法后不是立即可用的(因为该方法是并行运行的),因此使用ActiveResult来交付结果。
Poco::ActiveResult是一个类模板,为函数的返回类型实例化。
启动一个活动方法将返回一个Poco::ActiveResult,该Poco::ActiveResult中包含结果(或异常)。
使用wait()或tryWait()来等待结果,然后通过调用data()获得结果。

6.4.3 示例

$ vi activeMethod.cpp

讯享网#include "Poco/ActiveMethod.h" #include "Poco/ActiveResult.h" #include <utility> #include <iostream> using Poco::ActiveMethod; using Poco::ActiveResult; class ActiveAdder { 
    public: ActiveAdder():add(this, &ActiveAdder::addImpl){ 
   } ActiveMethod<int, std::pair<int, int>, ActiveAdder> add; private: int addImpl(const std::pair<int, int>& args) { 
    return args.first + args.second; } }; int main(int argc, char** argv) { 
    ActiveAdder adder; ActiveResult<int> sum = adder.add(std::make_pair(1, 2)); sum.wait(); std::cout << sum.data() << std::endl; return 0; } 

编译:

$ g++ activeMethod.cpp -I ~/git/poco/install/include -L ~/git/poco/install/lib -lPocoFoundationd -lpthread -std=c++11 

6.5 Poco::ActiveDispatcher 活动调度

6.5.1 什么是活动调度?

Poco::ActiveMethod的默认行为不符合活动对象的“经典”定义,即方法在单个线程中排队等待执行。
要获得经典行为,可以使用Poco::ActiveDispatcher作为活动对象的基类。
需要告诉Poco::ActiveMethod使用Poco::ActiveDispatcher来调度方法的执行。

6.5.2 示例

$ vi activeAdder.cpp

讯享网#include "Poco/ActiveMethod.h" #include "Poco/ActiveResult.h" #include "Poco/ActiveDispatcher.h" #include <utility> #include <iostream> using Poco::ActiveMethod; using Poco::ActiveResult; class ActiveAdder: public Poco::ActiveDispatcher { 
    public: ActiveAdder(): add(this, &ActiveAdder::addImpl){ 
   } ActiveMethod<int, std::pair<int, int>, ActiveAdder, Poco::ActiveStarter<Poco::ActiveDispatcher> > add; private: int addImpl(const std::pair<int, int>& args) { 
    return args.first + args.second; } }; int main(int argc, char** argv) { 
    ActiveAdder adder; ActiveResult<int> sum = adder.add(std::make_pair(1, 2)); sum.wait(); std::cout << sum.data() << std::endl; return 0; } 

编译:

$ g++ activeAdder.cpp -I ~/git/poco/install/include -L ~/git/poco/install/lib -lPocoFoundationd -lpthread -std=c++11 
小讯
上一篇 2025-03-04 14:02
下一篇 2025-04-02 19:00

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/41388.html