Creating a cache manager
The major question you should be asking is "what am I going to do with these bitmaps"? Threading is an issue when you have multiple threads writing the same data - multiple threads reading the same data (and only reading it) don't cause such problems. Whether you are going about this the correct way or not depends heavily on the answer to that question.

However, some of this code can be cleaned up and improved somewhat, regardless of that answer.

Calling mutex_.lock() and then explicitly calling mutex_.unlock() is dangerous. If anything in between those calls throws an exception, then your mutex will never be unlocked, and you'll have a deadlock. Solving this is easy, and just requires using what is known as RAII (Resource Acquisition is Initialization). In this case, this is done by using a std::lock_guard:

  static std::shared_ptr&lt;Bitmap&gt; loadBitmap(const std::string&amp; filePath) {
      std::lock_guard&lt;std::mutex&gt; guard(mutex_);
      ...
  } // mutex_ is unlocked as soon as it goes out of scope


This ensures that mutex_ will always be unlocked, no matter how the function exists (whether normally or through an exception).

You use auto in a few places, so you might as well simplify:

std::unordered_map&lt;std::string,std::shared_ptr&lt;Bitmap&gt;&gt;::iterator delIter = cache_.end();


to:

auto delIter = cache_.end();


The loadBitmap function itself has dubious design. It isn't just loading a bitmap, but is also trying to clean out the cache. Performing this second step should absolutely be broken out into a separate function:

void remove_from_cache()
{
    if (cache_.size() &gt;= kSlotThreshold) {
    auto delIter = cache_.end();
    for (auto iter = cache_.begin(); iter != cache_.end(); ++iter) {
        auto&amp; item = *iter;
        if (item.second &amp;&amp; item.second.use_count() == 1) {
            delIter = iter;
            break;
        }
    }
    if (cache_.end() != delIter) {
        (*delIter).second.reset();
        cache_.erase(delIter);
    }
}


The current cache size is very small (20 elements). If you expect this to remain small (&lt; 1000 elements), you may want to consider using a std::vector instead. At this size, constants involved in the O(1) lookup for a hashmap dominate, and std::vector will (potentially) be quicker. If you expect many more lookups than insertions, a sorted vector could also work.

The reason I suggest is that this would make clearing out the cache much easier: using a std::vector (which would look something like std::vector&lt;std::pair&lt;std::string, std::shared_ptr&lt;Bitmap&gt;&gt;&gt; (which could really do with a using statement or 3)), then erasing values is simply:

using pair_t = std::pair&lt;std::string, std::shared_ptr&lt;Bitmap&gt;&gt;;
using container_t = std::vector&lt;pair_t&gt;;
container_t cache;

void remove_from_cache()
{
    if(cache.size() &gt; kSlotThreshold) {
        cache.erase(std::remove_if(cache.begin(), cache.end(),
             [](const pair_t&amp; p) { return p.second.use_count() == 1; }),
             cache.end());
    }
}


Whether this removal algorithm is any good or not is up for some debate, you might want to look at something like an LRU (least recently used) cache instead - currently, there is no guarantee that your algorithm will remove things from the cache. Also, this would require a (very minor) change to your lookup code:

 auto iter = cache_.find(filePath);


would become:

 auto iter = std::find_if(cache.begin(), cache.end(), 
     [&amp;](const pair_t&amp; p) { return p.first == filePath; });


A few final small points:

Prefer to use std::make_shared instead of constructing the shared pointer directly, for example:

std::shared_ptr&lt;Bitmap&gt; newPtr(new Bitmap(filePath));


should be:

auto newPtr = std::make_shared&lt;Bitmap&gt;(filePath);


If you're defining a struct just to give things a scope to live in (and hence using static for everything), use a namespace instead.