#include "../Mutex.h"
#include "../Thread.h"
#include
#include
using namespace muduo;
class Foo
{
public:
void doit() const;
};
MutexLock mutex;
std::vector foos;
void post(const Foo& f)
{
MutexLockGuard lock(mutex);
foos.push_back(f);
}
void traverse()
{
MutexLockGuard lock(mutex);
for (std::vector::const_iterator it = foos.begin();
it != foos.end(); ++it)
{
it->doit();
}
}
void Foo::doit() const
{
Foo f;
post(f);
}
int main()
{
Foo f;
post(f);
traverse();
}
代码分析:上述代码有两个地方有问题
针对上述代码中出现的问题,将上述代码做出如下修改:
// ./muduo/recipes/thread/test/CopyOnWrite_test.cc
#include "../Mutex.h"
#include "../Thread.h"
#include
#include
#include
using namespace muduo;
class Foo
{
public:
void doit() const;
};
typedef std::vector FooList;
typedef boost::shared_ptr FooListPtr;
FooListPtr g_foos;
MutexLock mutex;
void post(const Foo& f)
{
printf("post\n");
MutexLockGuard lock(mutex);
if (!g_foos.unique())
{
g_foos.reset(new FooList(*g_foos));
printf("copy the whole list\n");
}
assert(g_foos.unique());
g_foos->push_back(f);
}
void traverse()
{
FooListPtr foos;
{
MutexLockGuard lock(mutex);
foos = g_foos;
assert(!g_foos.unique());
}
// assert(!foos.unique()); this may not hold
for (std::vector::const_iterator it = foos->begin();
it != foos->end(); ++it)
{
it->doit();
}
}
void Foo::doit() const
{
Foo f;
post(f);
}
int main()
{
g_foos.reset(new FooList);
Foo f;
post(f);
traverse();
}
总结:
#include "../Mutex.h"
#include "../Thread.h"
#include
#include
class Request;
class Inventory
{
public:
void add(Request* req)
{
muduo::MutexLockGuard lock(mutex_);
requests_.insert(req);
}
void remove(Request* req) __attribute__ ((noinline))
{
muduo::MutexLockGuard lock(mutex_);
requests_.erase(req);
}
void printAll() const;
private:
mutable muduo::MutexLock mutex_;
std::set requests_;
};
Inventory g_inventory;
class Request
{
public:
void process() // __attribute__ ((noinline))
{
muduo::MutexLockGuard lock(mutex_);
g_inventory.add(this);
// ...
}
~Request() __attribute__ ((noinline))
{
muduo::MutexLockGuard lock(mutex_);
sleep(1);
g_inventory.remove(this);
}
void print() const __attribute__ ((noinline))
{
muduo::MutexLockGuard lock(mutex_);
// ...
}
private:
mutable muduo::MutexLock mutex_;
};
void Inventory::printAll() const
{
muduo::MutexLockGuard lock(mutex_);
sleep(1);
for (std::set::const_iterator it = requests_.begin();
it != requests_.end();
++it)
{
(*it)->print();
}
printf("Inventory::printAll() unlocked\n");
}
/*
void Inventory::printAll() const
{
std::set requests
{
muduo::MutexLockGuard lock(mutex_);
requests = requests_;
}
for (std::set::const_iterator it = requests.begin();
it != requests.end();
++it)
{
(*it)->print();
}
}
*/
void threadFunc()
{
Request* req = new Request;
req->process();
delete req;
}
int main()
{
muduo::Thread thread(threadFunc);
thread.start();
usleep(500 * 1000);
g_inventory.printAll();
thread.join();
}
上述代码存在的问题:
thread.start()
后执行 threadFunc
函数,一个是 main 线程 执行 g_inventory.printAll()
。thread.start()
开始执行后,main 函数会 sleep 一小会儿,当 main 函数执行到 g_inventory.printAll();
这里的时候,thread 线程很可能已经开始执行 delete req;
此时可能会持有 Inventory 中的 mutex_ 锁并已经持有 Request 中的锁 mutex_;main 线程 执行 g_inventory.printAll();
时,会先持有 Inventory 中的锁,此时会造成死锁。(简言之,加锁顺序相反,造成死锁。)改进版:
// ./muduo/recipes/thread/test/RequestInventory_test.cc
#include "../Mutex.h"
#include "../Thread.h"
#include
#include
#include
class Request;
class Inventory
{
public:
Inventory()
: requests_(new RequestList)
{
}
void add(Request* req)
{
muduo::MutexLockGuard lock(mutex_);
if (!requests_.unique())
{
requests_.reset(new RequestList(*requests_));
printf("Inventory::add() copy the whole list\n");
}
assert(requests_.unique());
requests_->insert(req);
}
void remove(Request* req) // __attribute__ ((noinline))
{
muduo::MutexLockGuard lock(mutex_);
if (!requests_.unique())
{
requests_.reset(new RequestList(*requests_));
printf("Inventory::remove() copy the whole list\n");
}
assert(requests_.unique());
requests_->erase(req);
}
void printAll() const;
private:
typedef std::set RequestList;
typedef boost::shared_ptr RequestListPtr;
RequestListPtr getData() const
{
muduo::MutexLockGuard lock(mutex_);
return requests_;
}
mutable muduo::MutexLock mutex_;
RequestListPtr requests_;
};
Inventory g_inventory;
class Request
{
public:
Request()
: x_(0)
{
}
~Request() __attribute__ ((noinline))
{
muduo::MutexLockGuard lock(mutex_);
x_ = -1;
sleep(1);
g_inventory.remove(this);
}
void process() // __attribute__ ((noinline))
{
muduo::MutexLockGuard lock(mutex_);
g_inventory.add(this);
// ...
}
void print() const __attribute__ ((noinline))
{
muduo::MutexLockGuard lock(mutex_);
// ...
printf("print Request %p x=%d\n", this, x_);
}
private:
mutable muduo::MutexLock mutex_;
int x_;
};
void Inventory::printAll() const
{
RequestListPtr requests = getData();
sleep(1);
for (std::set::const_iterator it = requests->begin();
it != requests->end();
++it)
{
(*it)->print();
}
}
void threadFunc()
{
Request* req = new Request;
req->process();
delete req;
}
int main()
{
muduo::Thread thread(threadFunc);
thread.start();
usleep(500*1000);
g_inventory.printAll();
thread.join();
}
printAll()
函数中,使用 getData()
来获取 requests_
对象,相当于这个对象的引用计数 +1,本质上还是操作这个对象,所以依然存在一个线程析构这个对象的同时另一个线程调用这对象的函数(???是因为这个原因嘛)如下代码改进的点:
#include "../Mutex.h"
#include "../Thread.h"
#include
#include
#include
#include
class Request;
typedef boost::shared_ptr RequestPtr;
class Inventory
{
public:
Inventory()
: requests_(new RequestList)
{
}
void add(const RequestPtr& req)
{
muduo::MutexLockGuard lock(mutex_);
if (!requests_.unique())
{
requests_.reset(new RequestList(*requests_));
printf("Inventory::add() copy the whole list\n");
}
assert(requests_.unique());
requests_->insert(req);
}
void remove(const RequestPtr& req) // __attribute__ ((noinline))
{
muduo::MutexLockGuard lock(mutex_);
if (!requests_.unique())
{
requests_.reset(new RequestList(*requests_));
printf("Inventory::remove() copy the whole list\n");
}
assert(requests_.unique());
requests_->erase(req);
}
void printAll() const;
private:
typedef std::set RequestList;
typedef boost::shared_ptr RequestListPtr;
RequestListPtr getData() const
{
muduo::MutexLockGuard lock(mutex_);
return requests_;
}
mutable muduo::MutexLock mutex_;
RequestListPtr requests_;
};
Inventory g_inventory;
class Request : public boost::enable_shared_from_this
{
public:
Request()
: x_(0)
{
}
~Request()
{
x_ = -1;
}
void cancel() __attribute__ ((noinline))
{
muduo::MutexLockGuard lock(mutex_);
x_ = 1;
sleep(1);
printf("cancel()\n");
g_inventory.remove(shared_from_this());
}
void process() // __attribute__ ((noinline))
{
muduo::MutexLockGuard lock(mutex_);
g_inventory.add(shared_from_this());
// ...
}
void print() const __attribute__ ((noinline))
{
muduo::MutexLockGuard lock(mutex_);
// ...
printf("print Request %p x=%d\n", this, x_);
}
private:
mutable muduo::MutexLock mutex_;
int x_;
};
void Inventory::printAll() const
{
RequestListPtr requests = getData();
printf("printAll()\n");
sleep(1);
for (std::set::const_iterator it = requests->begin();
it != requests->end();
++it)
{
(*it)->print();
}
}
void threadFunc()
{
RequestPtr req(new Request);
req->process();
req->cancel();
}
int main()
{
muduo::Thread thread(threadFunc);
thread.start();
usleep(500*1000);
g_inventory.printAll();
thread.join();
}