1
\$\begingroup\$

I am learning concurrent programming and am writing a thread safe LRU cache for practice. Below is my first attempt. Kindly comment and let me know where I can improve.

PS: There have already been a few questions asked on this topic but a more toned down version will help me understand the underlying concept of concurrency.

#include "stdafx.h"
#include <iostream>
#include <unordered_map>
#include "thread"
#include "mutex"

class LRUCache
{

    std::mutex mu;

private:
    int cacheSize;
    std::unordered_map<int, std::pair<int, std::list<int>::iterator>> cache;
    std::list<int> lru;
    void update(std::unordered_map<int, std::pair<int, std::list<int>::iterator>>::iterator &it);

public:
    void setSize(int size);
    int get(int key);
    void set(int key, int value);
};

void LRUCache::setSize(int size)
{
    std::lock_guard <std::mutex> locker(mu);
    cacheSize = size;
}

int LRUCache::get(int key)
{
    std::lock_guard <std::mutex> locker(mu);
    auto item = cache.find(key);

    if (item == cache.end())
    {
        return -1;
    }

    update(item);

    return item->second.first;
}

void LRUCache::set(int key, int value)
{
    std::lock_guard <std::mutex> locker(mu);

    auto item = cache.find(key);

    if (item != cache.end())
    {
        update(item);
        cache[key] = { value, lru.begin() };
        return;
    }

    if (cache.size() == cacheSize)
    {
        cache.erase(lru.back());
        lru.pop_back();
    }

    lru.push_front(key);
    cache.insert({ key,{ value, lru.begin() } });
}

void LRUCache::update(std::unordered_map<int, std::pair<int, std::list<int>::iterator>>::iterator &it)
{
    lru.erase(it->second.second);
    lru.push_front(it->first);
    it->second.second = lru.begin();
}

void function2(LRUCache *lruCache)
{
    lruCache->set(5, 5);
    lruCache->set(6, 6);
    lruCache->set(1, 1);

    std::cout << "F2::Successful Execution: " << lruCache->get(4) << std::endl;

    lruCache->set(8, 8);

    std::cout << "F2::Successful Execution: " << lruCache->get(1) << std::endl;
}

void function1(LRUCache *lruCache)
{
    lruCache->set(1, 1);
    lruCache->set(2, 2);
    lruCache->set(3, 3);

    std::cout << "F1::Successful Execution: " << lruCache->get(3) << std::endl;

    lruCache->set(4, 4);

    std::cout << "F1::Successful Execution: " << lruCache->get(2) << std::endl;
}

int main() {

    LRUCache *lruCache = new LRUCache();
    lruCache->setSize(2);

    std::thread t1(function1, lruCache);
    t1.join();

    std::thread t2(function2, lruCache);
    t2.join();

    return 0;
}
\$\endgroup\$
2
  • \$\begingroup\$ What part, exactly, is "toned down"? Do you mean that you want the answers to be toned down in terms of their complexity? If so, you might consider indicating that by adding the [beginner] tag. Or maybe you just mean that your code/implementation is simpler than the other questions? (Also, please don't use blockquotes to quote yourself!) \$\endgroup\$ Commented Jan 5, 2017 at 11:10
  • \$\begingroup\$ The latter, simpler code implementations \$\endgroup\$ Commented Jan 5, 2017 at 14:08

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.