我有一个扮演网络服务器角色的应用程序,并将pthread_create多个线程,每个线程将侦听特定的TCP端口并接受多个TCP套接字连接。 现在,经过一段时间,例如60秒后,所有网络客户端(TCP套接字客户端)都已关闭(但我的应用程序仍在运行这些线程并监听这些端口),如何收集数据(例如收到的total_bytes)我的应用程序创建的那些线程?
我目前使用的一个解决方案是:在每个套接字accept()中,当新数据到达时,相应的线程将使用pthread_mutex_t更新静态变量。但我怀疑这是互操作的效率低下和浪费时间。
有没有无锁的方法吗? 如果有任何解决方案" per_cpu"计数器就像在网络驱动程序中使用/没有锁/互斥锁? 或者,当从socket(read())接收n个字节时,我不会更新Receiver_Total_Bytes。相反,我会继续计算线程中的总字节数。但问题是,如何从未完成的线程中获取总字节数?
=== sudo code ===
register long Receiver_Total_Bytes = 0;
static pthread_mutex_t Summarizer_Mutex = PTHREAD_MUTEX_INITIALIZER;
void add_server_transfer_bytes(register long bytes )
{
pthread_mutex_lock( &Summarizer_Mutex );
Receiver_Total_Bytes += bytes;
pthread_mutex_unlock( &Summarizer_Mutex );
}
void reset_server_transfer_bytes( )
{
pthread_mutex_lock( &Summarizer_Mutex );
Receiver_Total_Bytes = 0;
pthread_mutex_unlock( &Summarizer_Mutex );
}
然后在套接字读取:
if((n = read(i, buffer, bytes_to_be_read)) > 0) {
............
add_server_transfer_bytes(n);
答案 0 :(得分:1)
是的,您的担忧是有道理的。你可以做的最糟糕的事情是使用另一个答案中建议的互斥量。互斥体抢占线程,所以他们真的是多线程最大的敌人。可能会想到的另一件事是使用原子操作来递增(也在同一答案中提到)。确实是个糟糕的主意!原子操作在争用下执行非常差(原子增量实际上是一个循环,它将尝试增加直到成功)。因为在我所描述的情况下,我认为结果会很高,原子会表现不好。
原子和互斥体的另一个问题是强制执行内存排序并强加bariers。对表现来说不是一件好事!
问题的真正解决方案当然是让每个线程都使用它自己的私人计数器。它不是per-cpu,而是每个线程。线程完成后,可以累积这些计数器。
答案 1 :(得分:1)
另一个选择是为每个线程分配一个结构,并让该结构至少包括所需的计数器,比如connections
和total_bytes
。
线程本身应该使用原子内置函数来增加它们:
__sync_fetch_and_add(&(threadstruct->connections), 1);
__sync_fetch_and_add(&(threadstruct->total_bytes), bytes);
或
__atomic_fetch_add(&(threadstruct->connections), 1, __ATOMIC_SEQ_CST);
__atomic_fetch_add(&(threadstruct->total_bytes), bytes, __ATOMIC_SEQ_CST);
这些比非原子操作稍慢,但如果没有争用,则开销非常小。 (根据我的经验,高速缓存乒乓 - 当不同的CPU试图同时访问变量时 - 是一个重大的风险和现实的减速原因,但这里风险很小。最坏的情况是,只有当前线程和主线程可以同时访问变量。当然,主线程不应该经常计算摘要;比如说,每秒一次或两次就足够了。)
因为结构是在主线程中分配的,所以主线程也可以访问计数器。要收集总数,它将使用循环,并在循环内部
overall_connections += __sync_fetch_and_add(&(threadstruct[thread]->connections), 0);
overall_total_bytes += __sync_fetch_and_add(&(threadstruct[thread]->total_bytes), 0);
或
overall_connections += __atomic_load_n(&(threadstruct[thread]->connections));
overall_total_bytes += __atomic_load_n(&(threadstruct[thread]->total_bytes));
有关__atomic和__sync内置功能的详细信息,请参阅GCC手册。像英特尔CC这样的其他C编译器也提供这些 - 或至少用于;几年前我最后一次验证这一点。 __sync版本较旧(在较旧的编译器版本中得到更广泛的支持),但__atomic版本反映了C11中指定的内存模型,因此未来的C编译器更有可能支持它们。