比较好奇libsrt库的socket通讯如何实现的,就随意翻了翻socket通讯部分的代码,发现了一些技巧,特此记录。
libsrt库主要由C++开发的,看:
个别的是.c文件。
libsrt库的examples中的test-c-client.c和test-c-server.c共同组成了一个简单的srt客户端和server端:

只要是写过socket的,这些再简单不过了。
追踪:

继续追:
发现通讯部分都是调用的uglobal().xxxx();
没看uglobal()函数之前,疯狂的猜测:
1、难道是x实现的?
2、难道是xx实现的?
3、难道是xxx实现的?
看了代码才知道,原来用到了单例模式:

言归正传:
如果是Java的话,最常见单例模式的如下所写:
public class SingleTon {
private static final SingleTon instance = new SingleTon();
private SingleTon() {
}
public static SingleTon getInstance(){
return instance;
}
}
libsrt中C++的单例这样写的:
CUDTUnited& srt::CUDT::uglobal()
{
static CUDTUnited instance;
return instance;
}
这段代码就是实现了一个超级简单的单例,还运用了一个C++的特性:函数内的静态局部变量。
这段代码主要是把这个特性给整明白就可以了,我还有GPT-4o都觉得有如下好处:
- 1、延迟初始化:局部静态变量只在函数首次被调用时初始化。这意味着如果从未调用该函数,实例也不会被创建。这在某些情况下可以节省资源和时间,因为不需要在程序开始时就创建单例实例。
- 2、线程安全:从C++11开始,C++标准保证局部静态变量的初始化是线程安全的。也就是说,即使在多线程环境下,也只有一个线程能够初始化该变量,其他线程将等待初始化完成。这确保了单例模式在多线程环境中的安全性。
- 3、控制实例化时机:因为实例是在函数第一次调用时创建的,所以程序员可以更精确地控制实例化的时机。这对一些需要依赖其他组件初始化的单例实例尤其重要。
- 4、简化代码和封装:将静态变量声明在函数内部,可以将实例的创建和访问逻辑封装在一个地方,简化代码结构,使代码更加模块化和易于维护。
GPT-4o
请GPT-4o老师生成一个示例,展示为什么将静态变量写在函数内部更有优势:
#include <iostream>
#include <mutex>
// Singleton class example
class CUDTUnited {
public:
// Public method to access the singleton instance
static CUDTUnited& getInstance() {
static CUDTUnited instance; // Local static instance
return instance;
}
void connect(int u, const char* name, int namelen, bool forced_isn) {
std::cout << "Connecting: " << name << std::endl;
}
private:
// Private constructor to prevent instantiation
CUDTUnited() { std::cout << "CUDTUnited instance created." << std::endl; }
// Delete copy constructor and assignment operator
CUDTUnited(const CUDTUnited&) = delete;
CUDTUnited& operator=(const CUDTUnited&) = delete;
};
// Usage example
void exampleUsage() {
CUDTUnited& instance = CUDTUnited::getInstance();
instance.connect(1, "example", 7, true);
}
int main() {
exampleUsage();
exampleUsage(); // The instance will not be created again
return 0;
}
在上述代码中:
首次调用 getInstance 时,会输出 “CUDTUnited instance created.” 和 “Connecting: example”。
再次调用 exampleUsage 时,只会输出 “Connecting: example”,因为实例只会被创建一次。
对比:"函数外部静态变量"如果将静态变量写在函数外部,无法享受上述所有的好处,例如:
#include <iostream>
// Singleton instance declared outside of the function
static CUDTUnited instance; // Will be created at program startup
class CUDTUnited {
public:
static CUDTUnited& getInstance() {
return instance;
}
void connect(int u, const char* name, int namelen, bool forced_isn) {
std::cout << "Connecting: " << name << std::endl;
}
private:
CUDTUnited() { std::cout << "CUDTUnited instance created." << std::endl; }
CUDTUnited(const CUDTUnited&) = delete;
CUDTUnited& operator=(const CUDTUnited&) = delete;
};
void exampleUsage() {
CUDTUnited& instance = CUDTUnited::getInstance();
instance.connect(1, "example", 7, true);
}
int main() {
exampleUsage();
exampleUsage(); // The instance will not be created again
return 0;
}
在这种情况下:
程序启动时,无论是否使用 CUDTUnited 实例,都会创建实例。这可能会浪费资源。
线程安全问题:需要手动保证多线程环境下的线程安全,增加代码复杂性。
因此,将静态变量写在函数内部是单例模式实现的一种推荐方法,特别是从C++11开始,它提供了更好的延迟初始化和线程安全支持。


3598

被折叠的 条评论
为什么被折叠?



