我在一个mingw程序中工作,除了主执行之外,还有两个线程(pthread_t)。每个线程都包含一个无休止的循环,用于与外部仪器进行连续通信,每个通信都具有不同的速度和协议。主程序只是在运行时根据用户要求显示仪器数据。只有当main中的"keepalive"变量设置为0时,线程中的无休止循环才会退出。为了在线程之间交换数据,我想知道我所知道的两种方法的优缺点:
-
向线程传递参数(可以使用pthread_create:*arg的最后一个参数来传递结构)。
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
-
使用全局变量并控制并发(pthread_mutex_lock和pthread_mutex_unlock)
事实上,我同时使用这两种方法(主要是因为我需要一个早期的"工作程序版本")。作为变量交换的一个例子,我指的是:
- 我将"keepalive"变量定义为全局变量(以便在线程进入无休止循环时共享它)
- 我通过结构传递通信配置数据
- 我正在通过全局数组(在无休止的循环中更新)将仪器数据检索到main
- 我正在检索文件的句柄以及与结构的通信(最后,正确地关闭和释放所有内容)
对于使用全局变量与使用带原子变量的结构的缺点,我将不胜感激。(代码清洁度、速度、效率、cpu/内存使用率…)。
谢谢。
不应该使用全局变量几乎是普遍存在的(我想你可能还不明白extern
是干什么的。)
然后。。。
[..]因为我关心速度和效率。
您正在利用锁定来同步访问。因此,忘记它吧,由此引发的开销将掩盖其他任何事情。由于(链接时间)已知地址,使用全局可能会"更快"。不过,我严重怀疑这是否有任何意义在没有咨询探查器的情况下,不要过早地进行优化
全局变量的一个缺点是更难测试线程之间的通信。在传递struct
时,您可以轻松地启动另一个线程,并让该线程与要测试其通信的端点进行通信。您不需要为此更改任何一个端点,只需要更改这些线程的启动方式即可。
此外,由于类似的原因,扩展应用程序也不那么容易。假设您有两个线程,例如一个"搜索器"(用于搜索某些数据)和一个"索引器"(负责存储匹配项,并可能分离进一步的操作)。如果它们通过全局变量进行通信,您将如何添加另一个搜索器线程?您必须使用另一个全局来进行新搜索器和索引器之间的通信,复制大部分搜索器线程(所有更改的都是用于通信的变量)。与传递包含"通信通道"的结构相比:在这里,您甚至不需要触摸搜索器代码,只需启动另一个即可。(不过,必须更改索引器才能在两个示例中都具有多个输入源)。
最后,通过线程启动上的结构传递"通信通道",可以隐藏通信通道,从而限制哪些线程可以访问它。当试图找出谁从通道中写入/读取时,这可能非常有价值。("是哪个线程写的那个sh#t?")
如果可以的话,尽量避免使用全局变量,因为它们可能会导致并发问题,并降低代码的可读性。我建议创建一个包含原子类型变量的用户数据结构。类似这样的东西:
struct user_data {
atomic_bool myBool;
atomic_int myInt;
};
int main() {
pthread_t myThread;
struct user_data userdata = {
.myBool = &ATOMIC_VAR_INIT(false),
.myInt = &ATOMIC_VAR_INIT(0)
};
if (pthread_create(&myThread, NULL, myFunction, &userdata) {
// error handling
}
}
这将避免并发问题,并使您的代码可读性更强。要修改原子变量,请使用原子操作,例如:
atomic_storeatomic_load
atomic_fetch_add
这是对原子操作库的引用。