[项目][WebServer][TcpServer]详细讲解

DieSnowK 2024-09-16 13:03:01 阅读 54

单例模式的线程安全

需要双重判空指针,降低锁冲突的概率,提高性能原因1:

当第一次实例化单例时,可能有多个线程同时到来,并且svr指针为空这时他们就会去竞争锁,但只有一个线程会最快拿到锁,并且成功实例化出单例对象但此时如果不加双重判空指针,那些也进了第一层if判断的,仍然会去实例化出对象 原因2:

为了线程安全,必然要加锁,加锁之后再去判空但每次调用<code>GetInstance()都需要去获得锁,释放锁,效率低下此时再加一层外层if判空,这样就会避免后续调用GetInstance()时没必要的锁竞争

static const uint16_t PORT = 8090;

static const int BACKLOG = 128;

// 单例 -- 饿汉模式

class TcpServer

{

public:

static TcpServer* GetInstance(uint16_t port = PORT)

{

static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;

if(svr == nullptr) // 双重判空指针,降低锁冲突的概率,提高性能

{

// 注意线程安全

pthread_mutex_lock(&lock);

if(svr == nullptr)

{

svr = new TcpServer(port);

svr->Init();

}

pthread_mutex_unlock(&lock);

}

return svr;

}

void Init()

{

Socket();

Bind();

Listen();

LOG(INFO, "TcpServer Init ... Success");

}

void Socket()

{

_listenSock = socket(AF_INET, SOCK_STREAM, 0);

if(_listenSock < 0)

{

LOG(FATAL, "Socket Error");

exit(1);

}

// 设置端口复用

int opt = 1;

setsockopt(_listenSock, SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(opt));

LOG(INFO, "Create Listen Socket ... Success");

}

void Bind()

{

struct sockaddr_in local;

memset(&local, 0, sizeof(local));

local.sin_family = AF_INET;

local.sin_addr.s_addr = INADDR_ANY;

local.sin_port = htons(_port);

if(bind(_listenSock, (struct sockaddr*)&local, sizeof(local)) < 0)

{

LOG(FATAL, "Bind Error");

exit(2);

}

LOG(INFO, "Bind Socket ... Success");

}

void Listen()

{

if(listen(_listenSock, BACKLOG) < 0)

{

LOG(FATAL, "Listen Error");

exit(3);

}

LOG(INFO, "Listen Socket ... Success");

}

int Sock()

{

return _listenSock;

}

~TcpServer()

{

if(_listenSock >= 0)

{

close(_listenSock);

}

}

private:

TcpServer(uint16_t port)

: _port(port)

, _listenSock(-1)

{ }

TcpServer(const TcpServer&) = delete;

private:

uint16_t _port;

int _listenSock;

static TcpServer* svr;

};

TcpServer* TcpServer::svr = nullptr;



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。