使用这两个接口就可以了
功能:保证某个操作能在超时时间内返回
说明:shared_from_this保证在退出作用域的时候智能指针不被释放,在线程退出的时候再自动销毁。
每执行一次execTimeout都会启动一个新线程,在线程退出后自动回收所有资源,注意T类型需要能被拷贝赋值
解决问题:某些操作会导致当前线程被阻塞(特别是网络操作),而这些接口又是第三方库提供的我们没办法修改
不适用:操作非常频繁的接口;可能会被永久阻塞的接口
demo:
// 执行正常返回
int result1 = execTimeout<int>(3000, []() -> int {
return 1 + 2;
});
std::cout << "result1:" << result1 << std::endl;
// 执行超时(执行函数需要花费4s而超时时间是3s),返回默认值
int result2 = execTimeout<int>(3000, 0, []() -> int {
std::this_thread::sleep_for(std::chrono::seconds(4));
return 3 + 4;
});
std::cout << "result2:" << result2 << std::endl;
#pragma once
#include <condition_variable>
#include <mutex>
#include <memory>
#include <thread>
template<typename T>
T execTimeout(int msTimeout, const std::function<T()> &cb)
{
std::shared_ptr<TimeoutGuard<T>> a(new TimeoutGuard<T>(msTimeout));
return a->exec(cb);
}
template<typename T>
T execTimeout(int msTimeout, const T &defaultValue, const std::function<T()> &cb)
{
std::shared_ptr<TimeoutGuard<T>> a(new TimeoutGuard<T>(msTimeout, defaultValue));
return a->exec(cb);
}
//////////////////////////////////////////////////////////////////////////
template<typename T>
class TimeoutGuard : public std::enable_shared_from_this<TimeoutGuard<T>>
{
public:
TimeoutGuard(int msTimeout)
: msTimeout_(msTimeout), isOk_(false)
{
}
TimeoutGuard(int msTimeout, const T &defaultValue)
: msTimeout_(msTimeout), result_(defaultValue), isOk_(false)
{
}
TimeoutGuard(const TimeoutGuard&) = delete;
TimeoutGuard& operator=(const TimeoutGuard&) = delete;
T exec(const std::function<T()> &cb)
{
if (s_threadCount > 300) {
std::cerr << "[TimeoutGuard] thread count overload:" << s_threadCount << std::endl;
return result_;
}
cb_ = cb;
long long startTime = getCurrentMillisecond();
auto self(shared_from_this());
thread_.reset(new std::thread([self]() {
++s_threadCount;
std::cout << "[TimeoutGuard] thread start id:" << std::this_thread::get_id() << ", count:" << s_threadCount << std::endl;;
T t = self->cb_();
std::unique_lock<std::mutex> lock(self->mutex_);
self->isOk_ = true;
self->result_ = t;
self->cond_.notify_one();
--s_threadCount;
std::cout << "[TimeoutGuard] thread end id:" << std::this_thread::get_id() << ", count:" << s_threadCount << std::endl;
}));
thread_->detach();
std::unique_lock<std::mutex> lock(mutex_);
while (!isOk_ && startTime + msTimeout_ > getCurrentMillisecond()) {
cond_.wait_for(lock, std::chrono::milliseconds(100));
}
return result_;
}
long long getCurrentMillisecond()
{
auto duration = std::chrono::system_clock::now().time_since_epoch();
auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(duration).count();
return millis;
}
private:
std::mutex mutex_;
std::condition_variable cond_;
std::unique_ptr<std::thread> thread_;
std::function<T()> cb_;
T result_;
bool isOk_;
long long msTimeout_;
static int s_threadCount;
};
template<typename T>
int TimeoutGuard<T>::s_threadCount = 0;