Poco库异步执行框架设计与实现分析

Stella981
• 阅读 846

Poco是一个强大的,用于构建C++程序的类库,它提供了许多非常好用的部件来帮助我们更快的开发C++应用程序,异步执行的相关部件正是这些好用的部件中的一部分。这里我们来看一下Poco库的异步执行组件的实现。

Poco库的异步执行组件主要有TaskManager,Task,ThreadPool等等。这里就主要从这些组件着手进行分析。

TaskManager/Task的使用

首先来看一下TaskManager/Task的使用。

1. 要使用Poco库的TaskManager来帮我们管理异步执行的任务,我们首先需要创建一个类,也是我们的任务的抽象,继承Poco的Task类,override其中的virtual函数virtual void runTask(),并在runTask()的实现中包含我们希望去异步执行的代码。

2. 然后创建一个线程池对象ThreadPool。这一步可选。

3. 创建一个TaskManager类的对象,并将前一步创建的线程池对象传给TaskManager的构造函数。TaskManager管理的所有Task都将在这个线程池的线程中执行。如果没有为TaskManager创建单独的线程池,则所有Task都将在default的线程池中执行。

4. 创建一个Task的对象。这个Task对象应该通过operator new,在堆上创建。

5. 通过前面创建的TaskManager对象,启动执行创建的Task。在Task执行完之后,TaskManager自己会把Task对象给delete掉。

具体还是来看一下代码比较方便:

#include <iostream>

#include <Poco/TaskManager.h>
#include <Poco/Task.h>
#include <Poco/TaskNotification.h>
#include <Poco/Thread.h>
#include <Poco/ThreadPool.h>

using namespace Poco;
using namespace std;

class DemoTask : public Task {
 public:
    DemoTask();
    virtual ~DemoTask();
    virtual void runTask();
};

DemoTask::DemoTask()
        : Task("DemoTask") {
}

DemoTask::~DemoTask() {

}

void DemoTask::runTask() {
    Thread::sleep(3000);
    cout << "DemoTask runtask." << endl;
}

int main() {
    ThreadPool threadPool(2, 32);
    TaskManager taskManager(threadPool);
    DemoTask * demoTask = new DemoTask;
    taskManager.start(demoTask);
    taskManager.joinAll();
    cout << "Demo run end." << endl;
    return 0;
}

这就是使用TaskManager/Task执行异步任务的基本方法了。但TaskManager提供的功能却不只如此,它还可以在Task的状态发生改变时,通过Observer通知我们具体的用法如下:

class TaskObserver {
 public:
    TaskObserver();
    void handleTaskFinished(TaskStartedNotification *startedNotify);
    void handleTaskCancelled(TaskCancelledNotification *cancelledNotify);

    void handleTaskFinished(TaskFinishedNotification *completedNotify);

    void handleTaskFailed(TaskFailedNotification *failedNotify);
    void handleTaskProgressUpdated(TaskProgressNotification *progressNotify);
};

TaskObserver::TaskObserver() {

}

void TaskObserver::handleTaskFinished(TaskStartedNotification *startedNotify) {
    startedNotify->release();
}

void TaskObserver::handleTaskCancelled(TaskCancelledNotification *cancelledNotify) {
    cancelledNotify->release();
}

void TaskObserver::handleTaskFinished(TaskFinishedNotification *completedNotify) {
    cout << "Task finished." << endl;
    completedNotify->release();
}

void TaskObserver::handleTaskFailed(TaskFailedNotification *failedNotify) {
    failedNotify->release();
}

void TaskObserver::handleTaskProgressUpdated(TaskProgressNotification *progressNotify) {
    progressNotify->release();
}

int main() {
    ThreadPool threadPool(2, 32);
    TaskManager taskManager(threadPool);

    TaskObserver observer;
    Observer<TaskObserver, TaskFinishedNotification> finishedObserver(observer, &TaskObserver::handleTaskFinished);
    taskManager.addObserver(finishedObserver);

    DemoTask * demoTask = new DemoTask;
    taskManager.start(demoTask);

在前面那个demo的基础之上,注册observer,并接收TaskManager关于task状态变化的通知。编译运行上面的这段程序,可以看到如下的输出:

DemoTask runtask.
Task finished.
Demo run end.

这里要注意,在收到Notification,处理了事件之后,要主动地release传进来的Notification,否则会发生内存泄漏。后面我们会通过对代码的分析来更详细的了解这样做的原因。

TaskManager的实现

来看TaskManager的实现,同时也再次全面的看一下TaskManager提供的功能即接口。TaskManager这个class的定义如下:

namespace Poco {

class Notification;
class ThreadPool;
class Exception;

class Foundation_API TaskManager
/// The TaskManager manages a collection of tasks
/// and monitors their lifetime.
///
/// A TaskManager has a built-in NotificationCenter that
/// is used to send out notifications on task progress
/// and task states. See the TaskNotification class and its
/// subclasses for the various events that result in a notification.
/// To keep the number of notifications small, a TaskProgressNotification
/// will only be sent out once in 100 milliseconds.
{
 public:
    typedef AutoPtr<Task> TaskPtr;
    typedef std::list<TaskPtr> TaskList;

    TaskManager();
    /// Creates the TaskManager, using the
    /// default ThreadPool.

    TaskManager(ThreadPool& pool);
    /// Creates the TaskManager, using the
    /// given ThreadPool.

    ~TaskManager();
    /// Destroys the TaskManager.

    void start(Task* pTask);
    /// Starts the given task in a thread obtained
    /// from the thread pool.
    ///
    /// The TaskManager takes ownership of the Task object
    /// and deletes it when it it finished.

    void cancelAll();
    /// Requests cancellation of all tasks.

    void joinAll();
    /// Waits for the completion of all the threads
    /// in the TaskManager's thread pool.
    ///
    /// Note: joinAll() will wait for ALL tasks in the
    /// TaskManager's ThreadPool to complete. If the
    /// ThreadPool has threads created by other
    /// facilities, these threads must also complete
    /// before joinAll() can return.

    TaskList taskList() const;
    /// Returns a copy of the internal task list.

    int count() const;
    /// Returns the number of tasks in the internal task list.

    void addObserver(const AbstractObserver& observer);
    /// Registers an observer with the NotificationCenter.
    /// Usage:
    ///     Observer<MyClass, MyNotification> obs(*this, &MyClass::handleNotification);
    ///     notificationCenter.addObserver(obs);

    void removeObserver(const AbstractObserver& observer);
    /// Unregisters an observer with the NotificationCenter.

    static const int MIN_PROGRESS_NOTIFICATION_INTERVAL;

 protected:
    void postNotification(const Notification::Ptr& pNf);
    /// Posts a notification to the task manager's
    /// notification center.

    void taskStarted(Task* pTask);
    void taskProgress(Task* pTask, float progress);
    void taskCancelled(Task* pTask);
    void taskFinished(Task* pTask);
    void taskFailed(Task* pTask, const Exception& exc);

 private:
    ThreadPool& _threadPool;
    TaskList _taskList;
    Timestamp _lastProgressNotification;
    NotificationCenter _nc;
    mutable FastMutex _mutex;

    friend class Task;
};

//
// inlines
//
inline int TaskManager::count() const {
    FastMutex::ScopedLock lock(_mutex);

    return (int) _taskList.size();
}

}  // namespace Poco

1. TaskManager有两个构造函数,一个需要传入一个ThreadPool对象的引用作为参数,用于定制通过该TaskManager管理的Task将要运行于其中的线程池;另一个不需要传入任何参数,则通过该TaskManager管理的Task将运行于default ThreadPool中。

2. 如我们前面看到的那样,TaskManager的start(Task* pTask)函数用于启动一个Task的执行。

3. cancelAll()函数用于取消所有Task的执行,joinAll()则用于等待TaskManager的ThreadPool的所有线程执行完成。

4. taskList()和count()是两个getter函数,它们分别用于获取TaskManager内部task的列表,和内部task的个数。

5. addObserver(const AbstractObserver& observer)和removeObserver(const AbstractObserver& observer)分别用于添加和移除观察者。如前所见,我们可以用Observer来监听Task状态的改变。

6. postNotification(const Notification::Ptr& pNf)用于向TaskManager的NotificationCenter post一个notification。后面我们会看到,这个函数主要由Task的postNotification(Notification* pNf)函数调用。我们自己写的Task可以通过后者post notification,这样就可以定制我们自己的Task的notification类型及时机,以方便我们对与Task的状态行为等有着更多的控制。

7. taskStarted(Task* pTask)、taskProgress(Task* pTask, float progress)、taskCancelled(Task* pTask)、taskFinished(Task* pTask)和taskFailed(Task* pTask, const Exception& exc)这几个函数,主要是在TaskManager内部,用来针对Task状态的改变进行通知。

接着来看TaskManager的实现:

namespace Poco {

const int TaskManager::MIN_PROGRESS_NOTIFICATION_INTERVAL = 100000;  // 100 milliseconds

TaskManager::TaskManager()
        : _threadPool(ThreadPool::defaultPool()) {
}

TaskManager::TaskManager(ThreadPool& pool)
        : _threadPool(pool) {
}

TaskManager::~TaskManager() {
}

void TaskManager::start(Task* pTask) {
    TaskPtr pAutoTask(pTask);  // take ownership immediately
    FastMutex::ScopedLock lock(_mutex);

    pAutoTask->setOwner(this);
    pAutoTask->setState(Task::TASK_STARTING);
    _taskList.push_back(pAutoTask);
    try {
        _threadPool.start(*pAutoTask, pAutoTask->name());
    } catch (...) {
        // Make sure that we don't act like we own the task since
        // we never started it.  If we leave the task on our task
        // list, the size of the list is incorrect.
        _taskList.pop_back();
        throw;
    }
}

void TaskManager::cancelAll() {
    FastMutex::ScopedLock lock(_mutex);

    for (TaskList::iterator it = _taskList.begin(); it != _taskList.end(); ++it) {
        (*it)->cancel();
    }
}

void TaskManager::joinAll() {
    _threadPool.joinAll();
}

TaskManager::TaskList TaskManager::taskList() const {
    FastMutex::ScopedLock lock(_mutex);

    return _taskList;
}

void TaskManager::addObserver(const AbstractObserver& observer) {
    _nc.addObserver(observer);
}

void TaskManager::removeObserver(const AbstractObserver& observer) {
    _nc.removeObserver(observer);
}

void TaskManager::postNotification(const Notification::Ptr& pNf) {
    _nc.postNotification(pNf);
}

void TaskManager::taskStarted(Task* pTask) {
    _nc.postNotification(new TaskStartedNotification(pTask));
}

void TaskManager::taskProgress(Task* pTask, float progress) {
    FastMutex::ScopedLock lock(_mutex);

    if (_lastProgressNotification.isElapsed(MIN_PROGRESS_NOTIFICATION_INTERVAL)) {
        _lastProgressNotification.update();
        _nc.postNotification(new TaskProgressNotification(pTask, progress));
    }
}

void TaskManager::taskCancelled(Task* pTask) {
    _nc.postNotification(new TaskCancelledNotification(pTask));
}

void TaskManager::taskFinished(Task* pTask) {
    _nc.postNotification(new TaskFinishedNotification(pTask));

    FastMutex::ScopedLock lock(_mutex);
    for (TaskList::iterator it = _taskList.begin(); it != _taskList.end(); ++it) {
        if (*it == pTask) {
            _taskList.erase(it);
            break;
        }
    }
}

void TaskManager::taskFailed(Task* pTask, const Exception& exc) {
    _nc.postNotification(new TaskFailedNotification(pTask, exc));
}

}  // namespace Poco

两个构造函数没有什么值得特别说明的地方,来看start()函数的行为。在start()函数中主要做了这样的一些事情:

1. 立即获取task的所有权,即为Task在函数栈上创建一个智能指针,这样就可以保证后面不管发生了什么,都不会发生不预期的Task对象内存不预期泄漏。

2. 获取mutex,设置task的owner为this,设置task的状态为Task::TASK_STARTING,然后将task push进TaskManager的taskList中。

3. 委托_threadPool启动task的执行。可以看到在通过threadPool启动task,抛出了异常时,会将刚刚push进taskList的task给pop出来,以避免发生Task的内存泄漏,并将捕获到的异常再次抛出。

addObserver()、removeObserver()和postNotification()都是简单的将动作委托给notification center。

taskStarted()、taskProgress()、taskCancelled()、taskFinished()和taskFailed()这几个函数基本上主要都是new一个Notification,然后通过notification center post出去。这几个函数都没有通过TaskManager类的postNotification()函数来post notification。

在taskProgress()中,可以看到,这里会对发出TaskProgressNotification的频率做一些控制,以免不当的Task发出过多的Progress通知,消耗过多的资源。

在taskFinished()中,可以看到,在发出通知之外,还会将task从TaskManager的taskList中移除出去。如果应用的代码里没有额外引用task的地方的话,这里将是一个task生命周期的终点。这个函数之后,task会被delete掉,它的所有的资源也会得到释放。

最后再来看TaskManager的析够函数,可以看到在这个函数里是什么动作都没有。在start()函数中我们有看到,会将task的owner设置为this,而在这里却没有reset task的owner为null。这种做法所带来额问题就是,如果threadPool和task的生命周期比TaskManager长的话,在task中就有可能去访问一个已经被释放了的,不存在的TaskManager对象,访问野指针的后果自是不言自明。那threadPool和task的生命周期到底有没有可能比TaskManager长呢?threadPool和task都是从TaskManager的外部获取的,相信这种可能性还是很大的

这里都没有看到通知动作的触发,也就是对于taskStarted()、taskProgress()、taskCancelled()、taskFinished()和taskFailed()这几个函数的调用呢。其实通知都是在Task中进行的。接着我们就来看Task的实现。

Task的实现

先来看Task类的定义:

namespace Poco {

class TaskManager;
class Notification;
class NotificationCenter;

class Foundation_API Task: public Runnable, public RefCountedObject
/// A Task is a subclass of Runnable that has a name
/// and supports progress reporting and cancellation.
///
/// A TaskManager object can be used to take care of the
/// lifecycle of a Task.
{
public:
    enum TaskState
    {
        TASK_IDLE,
        TASK_STARTING,
        TASK_RUNNING,
        TASK_CANCELLING,
        TASK_FINISHED
    };

    Task(const std::string& name);
    /// Creates the Task.

    const std::string& name() const;
    /// Returns the task's name.

    float progress() const;
    /// Returns the task's progress.
    /// The value will be between 0.0 (just started)
    /// and 1.0 (completed).

    virtual void cancel();
    /// Requests the task to cancel itself. For cancellation
    /// to work, the task's runTask() method must periodically
    /// call isCancelled() and react accordingly.
    ///
    /// Can be overridden to implement custom behavior,
    /// but the base class implementation of cancel() should
    /// be called to ensure proper behavior.

    bool isCancelled() const;
    /// Returns true if cancellation of the task has been
    /// requested.
    ///
    /// A Task's runTask() method should periodically
    /// call this method and stop whatever it is doing in an
    /// orderly way when this method returns true.

    TaskState state() const;
    /// Returns the task's current state.

    void reset();
    /// Sets the task's progress to zero and clears the
    /// cancel flag.

    virtual void runTask() = 0;
    /// Do whatever the task needs to do. Must
    /// be overridden by subclasses.

    void run();
    /// Calls the task's runTask() method and notifies the owner
    /// of the task's start and completion.

protected:
    bool sleep(long milliseconds);
    /// Suspends the current thread for the specified
    /// amount of time.
    ///
    /// If the task is cancelled while it is sleeping,
    /// sleep() will return immediately and the return
    /// value will be true. If the time interval
    /// passes without the task being cancelled, the
    /// return value is false.
    ///
    /// A Task should use this method in favor of Thread::sleep().

    void setProgress(float progress);
    /// Sets the task's progress.
    /// The value should be between 0.0 (just started)
    /// and 1.0 (completed).

    virtual void postNotification(Notification* pNf);
    /// Posts a notification to the task manager's
    /// notification center.
    ///
    /// A task can use this method to post custom
    /// notifications about its progress.

    void setOwner(TaskManager* pOwner);
    /// Sets the (optional) owner of the task.

    TaskManager* getOwner() const;
    /// Returns the owner of the task, which may be NULL.

    void setState(TaskState state);
    /// Sets the task's state.

    virtual ~Task();
    /// Destroys the Task.

private:
    Task();
    Task(const Task&);
    Task& operator = (const Task&);

    std::string _name;
    TaskManager* _pOwner;
    float _progress;
    TaskState _state;
    Event _cancelEvent;
    mutable FastMutex _mutex;

    friend class TaskManager;
};

//
// inlines
//
inline const std::string& Task::name() const {
    return _name;
}

inline float Task::progress() const {
    FastMutex::ScopedLock lock(_mutex);

    return _progress;
}

inline bool Task::isCancelled() const {
    return _state == TASK_CANCELLING;
}

inline Task::TaskState Task::state() const {
    return _state;
}

inline TaskManager* Task::getOwner() const {
    FastMutex::ScopedLock lock(_mutex);

    return _pOwner;
}

}  // namespace Poco

Task继承自Runnable和RefCountedObject。继承Runnable是为了能放到ThreadPool上执行,而继承RefCountedObject,则是为了能用智能指针AutoPtr管理堆上分配的Task对象。

然后来看Task类的实现:

namespace Poco {

Task::Task(const std::string& name)
        : _name(name),
          _pOwner(0),
          _progress(0),
          _state(TASK_IDLE),
          _cancelEvent(false) {
}

Task::~Task() {
}

void Task::cancel() {
    _state = TASK_CANCELLING;
    _cancelEvent.set();
    if (_pOwner)
        _pOwner->taskCancelled(this);
}

void Task::reset() {
    _progress = 0.0;
    _state = TASK_IDLE;
    _cancelEvent.reset();
}

void Task::run() {
    TaskManager* pOwner = getOwner();
    if (pOwner)
        pOwner->taskStarted(this);
    try {
        _state = TASK_RUNNING;
        runTask();
    } catch (Exception& exc) {
        if (pOwner)
            pOwner->taskFailed(this, exc);
    } catch (std::exception& exc) {
        if (pOwner)
            pOwner->taskFailed(this, SystemException(exc.what()));
    } catch (...) {
        if (pOwner)
            pOwner->taskFailed(this, SystemException("unknown exception"));
    }
    _state = TASK_FINISHED;
    if (pOwner)
        pOwner->taskFinished(this);
}

bool Task::sleep(long milliseconds) {
    return _cancelEvent.tryWait(milliseconds);
}

void Task::setProgress(float progress) {
    FastMutex::ScopedLock lock(_mutex);

    _progress = progress;
    if (_pOwner)
        _pOwner->taskProgress(this, _progress);
}

void Task::setOwner(TaskManager* pOwner) {
    FastMutex::ScopedLock lock(_mutex);

    _pOwner = pOwner;
}

void Task::setState(TaskState state) {
    _state = state;
}

void Task::postNotification(Notification* pNf) {
    poco_check_ptr(pNf);

    FastMutex::ScopedLock lock(_mutex);

    if (_pOwner) {
        _pOwner->postNotification(pNf);
    }
}

}  // namespace Poco

TaskManager中通过threadPool的start()函数来启动一个task的执行。threadPool的start()的参数是一个Runnable,在threadPool的thread中会去执行这个Runnable的run()函数。

TaskManager的Task最为特别的地方也正在与它的run()函数的实现了,正是在这个函数里实现了对我们的任务生命周期管理的主要部分。可以看到在run()函数中:

1. 首先获取task的owner,也就是管理这个task的TaskManager。

2. 如果owner存在,则执行owner的taskStarted()发出通知,表示一个task要启动执行了。

3. 将task的状态更新为TASK_RUNNING,然后执行runTask()函数。runTask()函数中主要放我们自己的任务的逻辑。如果在runTask()函数中发生了异常,而owner又是存在的,则会调用owner的taskFailed()函数,以通知应用程序,task的执行发生了错误。

可以看到这里会捕获所有的异常。这样做也是为了防止由于应用程序的代码写的不好,而把Poco的TaskManager/ThreadPool/Task这个异步执行的框架给搞挂了。

4. 更新状态为TASK_FINISHED。如果owner存在,则执行owner的taskFinished()发出通知,表示一个task的执行结束了。如我们前面看到的,在TaskManager的taskFinished()中,会将task移出taskList,从而让Task可以被release调用。

然后来看Task的setProgress()。我们实现的任务可以借助这个函数通过TaskManager的notification center将任务的执行进度通知出去。这个函数和android的AsyncTask的publishProgress()及onProgressUpdate()还是颇有几分神似。只是在这里回调同样在线程池的线程里执行。

接着来看cancel()和isCancelled()函数。应用程序可以通过cancel()来干预一个task的生命周期及执行,即取消task的执行。在cancel()函数中主要就是更新了task的state,唤醒了等待在_cancelEvent上的task,并发出一个通知。如同所有其它的异步任务执行框架那样,如java的Executor,android的AsyncTask等,这里的*cancel()函数同样不能强制地终止一个task的执行,它也只是设置一个标记。要想对task的生命周期有一个更完善的管理,同样需要我们在实现*runTask()时,能够记得在适当的时候调用isCancelled()检查task是否已经被cancel掉,主动地清理资源并退出执行**。

另外值得注意的是sleep()函数,它会等在_cancelEvent****上休眠,如我们前面分析的,cancel()函数会唤醒这种休眠。因而如果在实现的任务中,有需要休眠的地方,最后使用Task的这个sleep()函数,这样显然更加方便任务的生命周期管理

向TaskManager注册的observer,会接收到TaskManager所有task的某一类型的通知。对于某些消息类型,这种做法有可能会带来一些额外不必要的低效。

Poco库的Notification

来看一下Poco库的Notification框架。如我们前面看到的,这个框架包括了NotificationCenter,Notification,AbstractObserver及Observer等class。我们创建一个NotificationCenter的对象,然后可以向这个对象注册/移除AbstractObserver,同时可以通过这个对象post notification出去,post了notification之后,之前注册的AbstractObserver的callback会被调到。

可见Poco库的Notification与android的Notification是大为不同的,这是一个比较典型的观察者模式的实现,这套东西倒是和java的Observable/Observer比较相似。可以将NotificationCenter理解为一个AbstractObserver的容器,比如list,或vector等。而post notification则是遍历这个容器中的所有元素,并执行每个元素的特定方法。

NotificationCenter是这一套东西的核心,因而就先来看这个类的定义及实现:

namespace Poco {

class AbstractObserver;

class Foundation_API NotificationCenter
{
public:
    NotificationCenter();
    /// Creates the NotificationCenter.

    ~NotificationCenter();
    /// Destroys the NotificationCenter.

    void addObserver(const AbstractObserver& observer);
    /// Registers an observer with the NotificationCenter.
    /// Usage:
    ///     Observer<MyClass, MyNotification> obs(*this, &MyClass::handleNotification);
    ///     notificationCenter.addObserver(obs);
    ///
    /// Alternatively, the NObserver template class can be used instead of Observer.

    void removeObserver(const AbstractObserver& observer);
    /// Unregisters an observer with the NotificationCenter.

    bool hasObserver(const AbstractObserver& observer) const;
    /// Returns true if the observer is registered with this NotificationCenter.

    void postNotification(Notification::Ptr pNotification);
    /// Posts a notification to the NotificationCenter.
    /// The NotificationCenter then delivers the notification
    /// to all interested observers.
    /// If an observer throws an exception, dispatching terminates
    /// and the exception is rethrown to the caller.
    /// Ownership of the notification object is claimed and the
    /// notification is released before returning. Therefore,
    /// a call like
    ///    notificationCenter.postNotification(new MyNotification);
    /// does not result in a memory leak.

    bool hasObservers() const;
    /// Returns true iff there is at least one registered observer.
    ///
    /// Can be used to improve performance if an expensive notification
    /// shall only be created and posted if there are any observers.

    std::size_t countObservers() const;
    /// Returns the number of registered observers.

    static NotificationCenter& defaultCenter();
    /// Returns a reference to the default
    /// NotificationCenter.

private:
    typedef SharedPtr<AbstractObserver> AbstractObserverPtr;
    typedef std::vector<AbstractObserverPtr> ObserverList;

    ObserverList _observers;
    mutable Mutex _mutex;
};

}  // namespace Poco

然后是实现:

namespace Poco {

NotificationCenter::NotificationCenter() {
}

NotificationCenter::~NotificationCenter() {
}

void NotificationCenter::addObserver(const AbstractObserver& observer) {
    Mutex::ScopedLock lock(_mutex);
    _observers.push_back(observer.clone());
}

void NotificationCenter::removeObserver(const AbstractObserver& observer) {
    Mutex::ScopedLock lock(_mutex);
    for (ObserverList::iterator it = _observers.begin(); it != _observers.end(); ++it) {
        if (observer.equals(**it)) {
            (*it)->disable();
            _observers.erase(it);
            return;
        }
    }
}

bool NotificationCenter::hasObserver(const AbstractObserver& observer) const {
    Mutex::ScopedLock lock(_mutex);
    for (ObserverList::const_iterator it = _observers.begin(); it != _observers.end(); ++it)
        if (observer.equals(**it))
            return true;

    return false;
}

void NotificationCenter::postNotification(Notification::Ptr pNotification) {
    poco_check_ptr(pNotification);

    ScopedLockWithUnlock<Mutex> lock(_mutex);
    ObserverList observersToNotify(_observers);
    lock.unlock();
    for (ObserverList::iterator it = observersToNotify.begin(); it != observersToNotify.end(); ++it) {
        (*it)->notify(pNotification);
    }
}

bool NotificationCenter::hasObservers() const {
    Mutex::ScopedLock lock(_mutex);

    return !_observers.empty();
}

std::size_t NotificationCenter::countObservers() const {
    Mutex::ScopedLock lock(_mutex);

    return _observers.size();
}

namespace {
static SingletonHolder<NotificationCenter> sh;
}

NotificationCenter& NotificationCenter::defaultCenter() {
    return *sh.get();
}

}  // namespace Poco

可以看到,基本上也就是一些比较标准的容器操作。 NotificationCenter 使用STL的vector来保存 AbstractObserver ,因而,这个类也主要是对vector的操作的封装。

Poco的NotificationCenter还会创建一个default的实例。

在postNotification()函数的实现中可以看到,发送通知,主要是调用AbstractObserver的notify()回调。

接着在来看一下Notification的定义和实现,显示class的定义:

class Foundation_API Notification: public RefCountedObject
/// The base class for all notification classes used
/// with the NotificationCenter and the NotificationQueue
/// classes.
/// The Notification class can be used with the AutoPtr
/// template class.
{
public:
    typedef AutoPtr<Notification> Ptr;

    Notification();
    /// Creates the notification.

    virtual std::string name() const;
    /// Returns the name of the notification.
    /// The default implementation returns the class name.

protected:
    virtual ~Notification();
};

}  // namespace Poco

然后是Notification类的实现:

namespace Poco {

Notification::Notification() {
}

Notification::~Notification() {
}

std::string Notification::name() const {
    return typeid(*this).name();
}

}  // namespace Poco

这个class倒是没有特别值得说明的地方。实际用到的Notification对象的意义,主要是它的子类赋予的。

然后是AbstractObserver,定义如下:

class Foundation_API AbstractObserver
/// The base class for all instantiations of
/// the Observer and NObserver template classes.
{
public:
    AbstractObserver();
    AbstractObserver(const AbstractObserver& observer);
    virtual ~AbstractObserver();

    AbstractObserver& operator = (const AbstractObserver& observer);

    virtual void notify(Notification* pNf) const = 0;
    virtual bool equals(const AbstractObserver& observer) const = 0;
    virtual bool accepts(Notification* pNf) const = 0;
    virtual AbstractObserver* clone() const = 0;
    virtual void disable() = 0;
};

实现如下:

namespace Poco {

AbstractObserver::AbstractObserver() {
}

AbstractObserver::AbstractObserver(const AbstractObserver& observer) {
}

AbstractObserver::~AbstractObserver() {
}

AbstractObserver& AbstractObserver::operator =(const AbstractObserver& observer) {
    return *this;
}

}  // namespace Poco

对于AbstractObserver这个class,也不再赘述。

接着在来看一个Poco库中,方便这套机制使用的class,也就是Observer类:

template<class C, class N>
class Observer : public AbstractObserver
/// This template class implements an adapter that sits between
/// a NotificationCenter and an object receiving notifications
/// from it. It is quite similar in concept to the 
/// RunnableAdapter, but provides some NotificationCenter
/// specific additional methods.
/// See the NotificationCenter class for information on how
/// to use this template class.
///
/// Instead of the Observer class template, you might want to
/// use the NObserver class template, which uses an AutoPtr to
/// pass the Notification to the callback function, thus freeing
/// you from memory management issues.
{
 public:
    typedef void (C::*Callback)(N*);

    Observer(C& object, Callback method)
            : _pObject(&object),
              _method(method) {
    }

    Observer(const Observer& observer)
            : AbstractObserver(observer),
              _pObject(observer._pObject),
              _method(observer._method) {
    }

    ~Observer() {
    }

    Observer& operator =(const Observer& observer) {
        if (&observer != this) {
            _pObject = observer._pObject;
            _method = observer._method;
        }
        return *this;
    }

    void notify(Notification* pNf) const {
        Poco::Mutex::ScopedLock lock(_mutex);

        if (_pObject) {
            N* pCastNf = dynamic_cast<N*>(pNf);
            if (pCastNf) {
                pCastNf->duplicate();
                (_pObject->*_method)(pCastNf);
            }
        }
    }

    bool equals(const AbstractObserver& abstractObserver) const {
        const Observer* pObs = dynamic_cast<const Observer*>(&abstractObserver);
        return pObs && pObs->_pObject == _pObject && pObs->_method == _method;
    }

    bool accepts(Notification* pNf) const {
        return dynamic_cast<N*>(pNf) != 0;
    }

    AbstractObserver* clone() const {
        return new Observer(*this);
    }

    void disable() {
        Poco::Mutex::ScopedLock lock(_mutex);

        _pObject = 0;
    }

 private:
    Observer();

    C* _pObject;
    Callback _method;
    mutable Poco::Mutex _mutex;
};

}  // namespace Poco

Observer是一个模板类。特别值得关注的是notify()函数。C++的dynamic_cast在一个对象不能转为目标类型时,会返回NULL,这个地方会在dynamic_cast返回NULL时,不将实际的通知发送出去,从而就实现了一个非常好的功能,也就是我们可以针对特定类型的Notification来注册Observer。或者说,尽管通过Observer类实现的观察者也会在NotificationCenter发出任何notification时被通知,但只有我们想要监听的特定类型的Notification事件才会实际被传递到我们app的code。

Done。

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
4个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Stella981 Stella981
3年前
KVM调整cpu和内存
一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid
Easter79 Easter79
3年前
Twitter的分布式自增ID算法snowflake (Java版)
概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
10个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这