程序设计模式

本文介绍了程序设计中的线程池、Singleton模式和抽象工厂模式。线程池通过维护一组线程来提高并发性能,避免频繁创建和销毁线程的开销。Singleton模式确保类只有一个实例并提供全局访问点,而抽象工厂模式则用于创建一系列相关的产品对象,无需指定其具体类。C++代码示例展示了如何实现这些模式。
摘要由CSDN通过智能技术生成

连接池和线程池

在程序设计中,线程池是一种软件设计模式,用于实现计算机程序中的执行并发。线程池通常也称为复制的工作程序,或工作人员工作组模型,维护着多个线程,等待任务分配给监督程序并发执行。通过维护线程池,该模型可提高性能并避免由于频繁创建和销毁短命任务的线程而导致执行延迟。可用线程的数量已调整为程序可用的计算资源,例如执行完成后的并行任务队列。
线程池的大小是为执行任务而保留的线程数。它通常是应用程序的可调参数,可以进行调整以优化程序性能。确定最佳线程池大小对于优化性能至关重要。

与为每个任务创建新线程相比,线程池的一个好处是线程创建和销毁开销仅限于池的初始创建,这可能导致更好的性能和更好的系统稳定性。就时间而言,创建和销毁线程及其相关资源可能是一个昂贵的过程。但是,过多的线程预留会浪费内存,并且在可运行线程之间进行上下文切换会导致性能下降。与另一个网络主机的套接字连接可能需要花费许多CPU周期才能删除和重新建立,可以通过将其与在一个以上网络事务过程中存在的线程相关联来更有效地维护该套接字连接。

即使预留线程启动时间,使用线程池也可能很有用。有一些线程池的实现使得在手动管理线程时比在轻松的时候更容易地将工作排队,控制并发并在更高的级别上同步线程。在这些情况下,使用的性能优势可能是次要的。

通常,线程池在单台计算机上执行。但是,线程池在概念上与服务器场有关,在服务器场中,主进程(可能是线程池本身)将任务分配给不同计算机上的工作进程,以提高整体吞吐量。令人尴尬的并行问题非常适合这种方法。

可以根据等待任务的数量在应用程序的生存期内动态调整线程的数量。例如,如果有大量网页请求进入,则网络服务器可以添加线程,而当这些请求逐渐减少时,可以删除线程。[争议–讨论]拥有更大线程池的成本是资源使用量的增加。用于确定何时创建或销毁线程的算法会影响整体性能:

创建过多的线程会浪费资源,并浪费时间创建未使用的线程。
销毁太多线程之后,在再次创建它们时需要更多时间。
太慢地创建线程可能会导致客户端性能下降(等待时间长)。
太慢地破坏线程可能会耗尽其他资源进程。

C ++线程池

在C ++中,线程池基本上是一个线程集合,使用的线程数是固定的。当我们要一起处理多个任务(同时运行多个线程)时,任务被分发给线程池中的各个线程。当没有任务时,线程池中的线程处于空闲状态;当任务到达时,该任务将被发送到线程池;并分配给该线程。待处理的任务将保留在队列中,等待线程释放。在C ++中,没有用于线程池的特定库。
下列代码产生一个线程池。

thread_pool.h

#include   <iostream>
#include   <thread>
#include   <vector>
#include   <mutex>
#include   <queue>
#include   <future>
#include   <functional>
#include   <condition_variable>


using namespace std;

class thread_pool {
public:
    //getInstance to allow the second constructor to be called
    static thread_pool& getInstance(int numThreads){
        static thread_pool instance(numThreads);

        return instance;
    }

    thread_pool(uint8_t numThreads) : numOfThreads(numThreads) {
        Pool.reserve(numThreads);
        for(int i = 0; i != numThreads; ++i){
            Pool.emplace_back(thread(&thread_pool::thread_worker, this));
            Pool.back().detach();
        }
    }

    thread_pool() = delete;							// disable default constructor
    thread_pool(const thread_pool& other) = delete;   // disable copy constructor
    void operator=(const thread_pool& other) = delete;   // disable assinment operator

    template <typename Func, typename... Args >
    inline auto push(Func&& f, Args&&... args){
        typedef decltype(f(args...)) retType;

        packaged_task<retType()> task(move(bind(f, args...)));
        unique_lock<mutex> lock(JobMutex);
        future<retType> future = task.get_future();
        jobQ.emplace( make_shared<AnyJob<retType> > (move(task)) );
        cond_var.notify_one();

        return future;
    }

private:
    class Job {
    public:
        virtual ~Job()=default;
        virtual void execute() = 0;
    };

    template <typename RetType>
    class AnyJob : public Job {
    public:
        AnyJob(packaged_task<RetType()> func) : func(move(func)) {}
		
        void execute() {
			cout << "Thread ID: "<< this_thread::get_id() << endl;
            func();
        }

    private:
        packaged_task<RetType()> func;
    };

    uint8_t numOfThreads;              // number of threads in the pool
    vector<thread> Pool;        	 // the actual thread pool
    queue<shared_ptr<Job>> jobQ;

    condition_variable  cond_var;    // used to notify threads about available jobs
    mutex JobMutex;

    /* worker thread function */
    inline void thread_worker() {		
        while (true) {
            unique_lock<mutex> lock(JobMutex);
            cond_var.wait(lock, [this] {return !jobQ.empty(); });

            (jobQ.front())->execute();

            jobQ.pop();
        }
    }
};

线程池中的每一个线程是一个循环语句构成的worker线程函数,它从工作队列中取出下一个需要运行的任务。
条件变量, condition_variable cond_var和mutex, mutex JobMutex实现任务资源管理,和worker线程之间的互斥存取。
当需要运行一个新任务时,使用线程池的push函数,将任务加入到线程池中;线程池将该任务分配给某个线程;该线程运行该任务,并通过future,返回结果。

应用线程池代码

#include <iostream>

#include "thread_pool.h"

using namespace std;

int main(){
    thread_pool &pool = thread_pool::getInstance(3); //create pool with 3 threads
    auto testFunc = [](int x){ return x*x; };
    auto returnValue = pool.push(testFunc, 5);
    cout << returnValue.get() << endl;

	auto output_string = [](string outstr) {cout << "Output: " << outstr << endl;};
    pool.push(output_string, "hello world");
	
	auto circle_area = [](float r) { return 3.1415 * r * r;};
    auto ret_2 = pool.push(circle_area, 2.15);
	auto area = ret_2.get();
	cout << "Circle Area: " << area << endl;

	auto triangle = [](double base, double height)->double { return base * height / 2;};
    auto ret_3 = pool.push(triangle, 2.15, 3.5);
	auto area_3 = ret_3.get();
	cout << "Triangle  Area: " << area_3 << endl;
	
    return 0;
}

输出结果

Thread ID: 139700179502848
25
Thread ID: 139700171048704
Output: hello world
Thread ID: 139700171048704
Circle Area: 14.5216
Thread ID: 139700179502848
Triangle  Area: 3.7625

通过调用线程池的push函数,该应用将需要运行的任务加入到线程池。如果需要返回值,通过push的返回future变量;取回任务的返回值。

Singleton

Singleton是一种创新的设计模式,可确保仅存在此类对象,并为其他任何代码提供对它的单点访问。
Singleton具有与全局变量几乎相同的优缺点。尽管它们非常方便,但它们破坏了代码的模块化。
您不能仅在其他情况下使用依赖于Singleton的类。您还必须参加Singleton课程。在大多数情况下,此限制是在创建单元测试期间出现的。

Singleton是一种创新性的设计模式,可让您确保一个类只有一个实例,同时提供对该实例的全局访问点。

Singleton模式同时解决了两个问题,这违反了单一责任原则

  1. 确保一个类只有一个实例。为什么要控制一个类有多少个实例?最常见的原因是,控制对某些共享资源(例如数据库或文件)的访问。
    工作原理如下:假设您创建了一个对象,但过了一会儿决定创建一个新对象。您将得到一个已经创建的对象,而不是接收一个新鲜的对象。
    使用常规构造函数无法实现此行为,因为构造函数调用必须始终按设计返回新对象。
  2. 提供对该实例的全局访问点。还记得您(好的,我)用来存储一些基本对象的那些全局变量吗?尽管它们非常方便,但是它们也非常不安全,因为任何代码都可能会覆盖这些变量的内容并使应用程序崩溃。
    就像全局变量一样,Singleton模式使您可以从程序中的任何位置访问某些对象。但是,它也可以防止该实例被其他代码覆盖。
    这个问题还有另一面:您不希望将解决问题#1的代码散布到整个程序中。最好将它放在一个类中,尤其是如果其余代码已经依赖于它。

Singleton模式代码

#include <iostream>
#include <mutex>
#include <thread>

using namespace  std;

class Singleton
{
private:
    static Singleton *m_pInst;
    static mutex inst_mutex;

protected:
    Singleton(const string value): val_str(value)
    {
    }
    ~Singleton() = default;
    string val_str;

public:
    Singleton(Singleton &other) = delete;
    void operator=(const Singleton &) = delete;

    static Singleton *GetInstance(const std::string& value);
    
    string value() const{
        return val_str;
    } 
};

Singleton* Singleton::m_pInst{nullptr};
mutex Singleton::inst_mutex;

Singleton *Singleton::GetInstance(const std::string& value)
{
    lock_guard<std::mutex> lock(inst_mutex);
    if (m_pInst == nullptr)
    {
        m_pInst = new Singleton(value);
    }
    return m_pInst;
}

void ThreadFoo(){
    // Following code emulates slow initialization.
    this_thread::sleep_for(chrono::milliseconds(1000));
    Singleton* singleton = Singleton::GetInstance("FOO");
    cout << singleton->value() << "\n";
}

void ThreadBar(){
    // Following code emulates slow initialization.
    this_thread::sleep_for(chrono::milliseconds(1000));
    Singleton* singleton = Singleton::GetInstance("BAR");
    cout << singleton->value() << "\n";
}

int main()
{   
    thread t1(ThreadFoo);
    thread t2(ThreadBar);
    t1.join();
    t2.join();
    
    return 0;
}

与全局变量不同,Singleton模式可确保一个类只有一个实例。除了Singleton类本身之外,没有任何其他东西可以替代缓存的实例。

请注意,您始终可以调整此限制,并允许创建任意数量的Singleton实例。唯一需要更改的代码就是getInstance方法的主体。

抽象工厂

抽象工厂是一种创建设计模式,它解决了在不指定具体类的情况下创建整个产品系列的问题。

抽象工厂定义了用于创建所有不同产品的接口,但将实际产品创建留给了具体的工厂类。每个工厂类型都对应于某个产品品种。

客户端代码调用工厂对象的创建方法,而不是通过构造函数调用(新运算符)直接创建产品。由于工厂对应于单个产品变型,因此其所有产品都是兼容的。

客户端代码只能通过工厂和产品的抽象接口来使用。这使客户端代码可以使用由工厂对象创建的任何产品变种。只需创建一个新的具体工厂类,然后将其传递给客户端代码即可。

抽象工厂代码

#include   <iostream>
#include   <memory>

using namespace std;

class VideoProduct {
public:
  virtual ~VideoProduct(){};
  virtual string watch() const = 0;
};

class  TV : public VideoProduct {
public:
  string watch() const override {
      return "Watch Video On TV";
  }
};

class Camera : public VideoProduct {
  string watch() const override {
    return "Watch Photo on Camera";
  }
};

class AudioProduct {
public:
  virtual ~AudioProduct(){};
  virtual string listen() const = 0;
};

class walkman : public AudioProduct {
public:
  string listen() const override {
     return "Listen broadcast on Walkman";
  }
};

class ipod : public AudioProduct {
public:
  string listen() const override {
    return "Listen MP3 music on ipod";
  }
};

class AbstractFactory {
public:
  virtual shared_ptr<VideoProduct> CreateVideo() const = 0;
  virtual shared_ptr<AudioProduct> CreateAudio() const = 0;
};

class Factory_SanJose : public AbstractFactory {
public:
  shared_ptr<VideoProduct> CreateVideo() const override {
	  shared_ptr<TV> p = make_shared<TV>();
	  return p;
  }

  shared_ptr<AudioProduct> CreateAudio() const override {
	  shared_ptr<AudioProduct> p = make_shared<walkman>();
	  return p;
  }
};

class Factory_LosAngles : public AbstractFactory {
public:

  shared_ptr<VideoProduct> CreateVideo() const override {
	 shared_ptr<VideoProduct> p = make_shared<Camera>();
	 return p;
  }

  shared_ptr<AudioProduct> CreateAudio() const override {
	 shared_ptr<AudioProduct> p = make_shared<ipod>();
	 return p;
  }
};

void ClientCode(shared_ptr<AbstractFactory> factory) {
  shared_ptr<VideoProduct> video = factory->CreateVideo();
  shared_ptr<AudioProduct> audio = factory->CreateAudio();
  
  cout << video->watch() << "\n";
  cout << audio->listen() << "\n";
}

int main() {
  cout << "Client: San Jose factory\n";
  shared_ptr<AbstractFactory> f1 = make_shared<Factory_SanJose>();
  ClientCode(f1);
  cout << endl;
  
  cout << "Client: Los Angles factory\n";
  shared_ptr<AbstractFactory> f2 = make_shared<Factory_LosAngles>();
  ClientCode(f2);

  return 0;
}

工厂模式(Factory Method)

工厂方法是一种创建设计模式,它解决了在不指定产品具体类的情况下,创建产品对象的问题。

工厂方法定义了一个方法,该方法应用于创建对象,而不是直接构造函数调用。子类可以重写此方法以更改将要创建的对象的类。

C ++代码中广泛使用了工厂方法模式。当需要为代码提供高度的灵活性时,此功能非常有用。

工厂方法可以由创建方法识别,创建方法从具体的类创建对象,但将它们作为抽象类型或接口的对象返回。

工厂模式代码

#include   <iostream>
#include   <memory>

using namespace std;

class Vehicle {
public:
  virtual ~Vehicle() = default;
  virtual string Operation() const = 0;
};

class SUV : public Vehicle {
public:
  string Operation() const override {
    return "SUV";
  }
};

class Truck : public Vehicle {
public:
  string Operation() const override {
    return "Truck";
  }
};

class Factory {
public:
  virtual ~Factory()=default;
  virtual shared_ptr<Vehicle> CreateVehicle() const = 0;

  string SomeOperation() const {
     shared_ptr<Vehicle> Vehicle = this->CreateVehicle();
     string result = "Factory:  " + Vehicle->Operation();
     return result;
  }
};

class SUVFactory : public Factory {
public:
  shared_ptr<Vehicle> CreateVehicle() const override {
      shared_ptr<Vehicle> p = make_shared<SUV>();
	  return p;
  }
};

class TruckFactory : public Factory {
 public:
  shared_ptr<Vehicle> CreateVehicle() const override {
      shared_ptr<Vehicle> p = make_shared<Truck>();
	  return p;
  }
};

void ClientCode(shared_ptr<Factory> f) {
  std::cout << f->SomeOperation() << endl;
}


int main() {
  cout << "Launched with the SUVFactory.\n";
  shared_ptr<Factory> f1 = make_shared<SUVFactory>();
  ClientCode(f1);
  cout << std::endl;
  
  cout << "Launched with the TructFactory.\n";
  shared_ptr<Factory> f2 = make_shared<TruckFactory>();
  ClientCode(f2);

  return 0;
}

输出结果

Launched with the SUVFactory.
Factory:  SUV

Launched with the TructFactory.
Factory:  Truck
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值