异常用来做错误处理的时候,程序到处都是 try cache ,代码十分的丑陋,我是不怎么喜欢的,我喜欢 asio 那种用 error_code 汇报错误 —— 不传 ec 的时候就抛异常,传就不抛,改为写入错误到 ec。
但是,异常用来做流程控制,又特别的好用。流程控制,无非顺序、选择、分支和循环。在 c++里,又比 C 多了一个异常。在嵌套很深的地方,跳出逻辑,除了异常,就没有其他更好的办法了。
在编写软件的时候,时常需要对一些数据做 cache。在使用的时候,要先检查 cache,存在则使用 cache ,不存在则按照老办法办,然后存入 cache。
每次使用前都进行判断, 污染了快速路径的代码,对有简洁洁癖的程序员来说,内心是十分的纠结的。
这个时候, 你就需要 异常。将 cache hit 作为正常的流程进行编写, 假定全部的数据都是在 cache 里的。这万一发生了 cache miss , 则抛出异常,并在异常处理重新载入数据。然后重启处理。
说到重启处理, 在 Windows 的 SEH 里,存在 EXECEPT_CONTINUE_EXECUTION
这个异常处理的结果, windows 看到异常处理函数返回这个,就会回到发生异常的地方重新执行。然而这毕竟是一个 Windows 系统特有的 SEH , 而且依赖底层CPU提供的机制。 编写C++是断然不能使用这套机制的。
思考的最终结果,就是下面这样的结构
for (int =0; i < retry_times ; i++)
{
try
{
auto v = cache_map_sometype.get_cache(key);
// process with v ....
........
}
catch(cache_miss&)
{
// load v from other resources, database, filesystem, network, etc.
......
cache_map_sometype.add_cache(key, v);
contine; // NOTE about this.
}
break;
}
在正常处理流程里, 执行到最后会有个 break 退出 for 循环。所以 for 循环在 cache hit 的状态下只执行一次。在 cache miss 的时候, catch block 里最后有一行 continue 。 于是就重启处理过程了。
下面给出 cache_map 的代码。
#pragma once #include <tuple> #include <map> #include <boost/thread.hpp> #include <boost/thread/shared_mutex.hpp> #include <boost/date_time/posix_time/ptime.hpp> struct cache_miss {}; template<typename KeyType, typename ValueType, int cache_aging_time = 30> class cache_map : protected std::map<KeyType, std::tuple<ValueType, boost::posix_time::ptime>> { typedef std::map<KeyType, std::tuple<ValueType, boost::posix_time::ptime>> base_type; public: ValueType get_cache(const KeyType& key) throw(cache_miss) { boost::shared_lock<boost::shared_mutex> l(m_mutex); typename base_type::iterator it = base_type::find(key); if (it == base_type::end()) { throw cache_miss(); } std::tuple<ValueType, boost::posix_time::ptime> & value_pack = it->second; auto should_be_after = boost::posix_time::second_clock::universal_time() - boost::posix_time::seconds(cache_aging_time); if (std::get<1>(value_pack) > should_be_after) return std::get<0>(value_pack); throw cache_miss(); } void remove_cache(const KeyType& k) { boost::unique_lock<boost::shared_mutex> l(m_mutex); base_type::erase(k); } ValueType get_cache(const KeyType& key) const throw(cache_miss) { boost::shared_lock<boost::shared_mutex> l(m_mutex); typename base_type::const_iterator it = base_type::find(key); if (it == base_type::end()) { throw cache_miss(); } const std::tuple<ValueType, boost::posix_time::ptime> & value_pack = it->second; auto should_be_after = boost::posix_time::second_clock::universal_time() - boost::posix_time::seconds(cache_aging_time); if (std::get<1>(value_pack) > should_be_after) return std::get<0>(value_pack); throw cache_miss(); } void add_to_cache(const KeyType& k , const ValueType& v) { boost::unique_lock<boost::shared_mutex> l(m_mutex); base_type::erase(k); base_type::insert(std::make_pair(k, std::make_tuple(v, boost::posix_time::second_clock::universal_time()))); } void tick() { boost::upgrade_lock<boost::shared_mutex> readlock(m_mutex); std::shared_ptr<boost::upgrade_to_unique_lock<boost::shared_mutex>> writelock; auto should_be_after = boost::posix_time::second_clock::universal_time() - boost::posix_time::seconds(30); for (auto it = base_type::begin(); it != base_type::end(); ) { const std::tuple<ValueType, boost::posix_time::ptime> & value_pack = it->second; if (std::get<1>(value_pack) < should_be_after) { if (!writelock) writelock.reset(new boost::upgrade_to_unique_lock<boost::shared_mutex>(readlock)); base_type::erase(it++); } else it++; } } private: mutable boost::shared_mutex m_mutex; }; template<typename KeyType, typename ValueType> class cache_map <KeyType, ValueType, 0> : protected std::map<KeyType, ValueType> { typedef std::map<KeyType, ValueType> base_type; public: ValueType& get_cache(const KeyType& key) throw(cache_miss) { boost::shared_lock<boost::shared_mutex> l(m_mutex); auto it = base_type::find(key); if (it == base_type::end()) { throw cache_miss(); } return it->second; } void add_to_cache(const KeyType& k , const ValueType& v) { boost::unique_lock<boost::shared_mutex> l(m_mutex); base_type::erase(k); base_type::insert(std::make_pair(k, v)); } void tick() { } private: mutable boost::shared_mutex m_mutex; };Comments