当前位置: 首页 > article >正文

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

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

1、线程

1.1 所属库、头文件

Poco中线程类是Poco::Thread,在基础库Foundation中,对应动态库libPocoFoundation.so;
使用时,需要包含头文件:#include “Poco/Thread.h”

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是一个类模板,也是一个适配器,它可以将无参数的成员函数放入线程中运行。
原理:Poco::RunnableAdapter继承自Poco::Runnable,在Poco::RunnableAdapter构造函数中指定了类和方法;然后在run()中调用了该方法,伪代码如下:

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)互斥锁:同一个互斥锁可以被同一个线程多次锁定(但不能被其他线程锁定)

相关函数:
lock():获取互斥锁,如果互斥锁被其他线程持有,则等待;
lock(long millisecs):获取互斥锁,如果互斥锁由另一个线程持有,则阻塞到给定的毫秒数。超时则抛出TimeoutException;
unlock():释放互斥锁,使它可以被另一个线程获取;
tryLock():尝试获取互斥锁。如果互斥锁由另一个线程持有,则立即返回false;如果互斥锁已被获取,则立即返回true。
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 用法

头文件:#include “Poco/Event.h”
Poco::Event支持自动复位和手动复位:

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

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

(未完,待续……)

5.4 Poco::Condition

5.5 Poco::Semaphore

5.6 Poco::RWLock

6、高级线程


http://www.kler.cn/a/147061.html

相关文章:

  • (一)- DRM架构
  • IDEA优雅debug
  • 微服务即时通讯系统的实现(客户端)----(3)
  • 文心一言编写小球反弹程序并优化
  • SpringBoot实现WebSocket
  • react中如何在一张图片上加一个灰色蒙层,并添加事件?
  • iview table 默认排序字段不高亮解决办法
  • Elasticsearch:什么是非结构化数据?
  • css实现图片绕中心旋转,鼠标悬浮按钮炫酷展示
  • android 9 adb安装过程学习(三)
  • TS 函数及多态
  • Windows10-用户账户控制、Windows远程桌面
  • 解决:前端js下载文件流出现“未知文件格式”错误
  • C语言重点编程题——11-20
  • Android 编译系统AIDL模块couldn‘t find import for class错误
  • leetcode42接雨水问题
  • Javascript每天一道算法题(十八)——矩阵置零-中等
  • 带你用uniapp从零开发一个仿小米商场_10. 首页开发
  • MATLAB算法实战应用案例精讲-【图像处理】图像增强
  • go 在使用Elasticsearch 聚合查询时 如何设置使用中国时区
  • C语言第三十五弹---打印九九乘法表
  • 【JMeter】不同场景下的接口请求
  • Sass基础知识详细讲解【附带表图】
  • ubuntu22.04 安装 jupyterlab
  • C#中警告IDE0290、IDE1006、IDE1100、IDE0251、IDE0300及处理
  • flutter 输入框组件 高度问题