Linux,C:从多个线程累积数据

时间:2015-08-27 20:35:36

标签: c linux multithreading networking

我有一个扮演网络服务器角色的应用程序,并将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);

2 个答案:

答案 0 :(得分:1)

是的,您的担忧是有道理的。你可以做的最糟糕的事情是使用另一个答案中建议的互斥量。互斥体抢占线程,所以他们真的是多线程最大的敌人。可能会想到的另一件事是使用原子操作来递增(也在同一答案中提到)。确实是个糟糕的主意!原子操作在争用下执行非常差(原子增量实际上是一个循环,它将尝试增加直到成功)。因为在我所描述的情况下,我认为结果会很高,原子会表现不好。

原子和互斥体的另一个问题是强制执行内存排序并强加bariers。对表现来说不是一件好事!

问题的真正解决方案当然是让每个线程都使用它自己的私人计数器。它不是per-cpu,而是每个线程。线程完成后,可以累积这些计数器。

答案 1 :(得分:1)

另一个选择是为每个线程分配一个结构,并让该结构至少包括所需的计数器,比如connectionstotal_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编译器更有可能支持它们。