【问题标题】:How to detect where data is lost when multithreading is used使用多线程时如何检测数据丢失的位置
【发布时间】:2016-02-17 05:26:46
【问题描述】:

我有一个程序可以让多个线程将条目插入哈希表并检索它们。哈希表本身是一个非常简单的实现,它有一个定义每个桶条目的结构和一个保存每个桶的表(数组)。我对并发和多线程非常陌生,但我认为为了避免在插入和读取操作期间数据在表中丢失,需要添加某种同步(以互斥锁之类的形式)以避免一个进程的数据操作被另一个进程抢占。

但在实践中,我不确定如何判断在哈希表上的数据读取或写入操作中进程可以在何处被抢占,以及应该在何处放置锁以避免此类问题以及死锁。根据this 网站,对于哈希表插入方法,我在将每个键插入表之前添加了一个互斥锁,并在函数结束时将其解锁。我基本上在从哈希表读取数据的函数中做了类似的事情,当我运行代码时,最初似乎成功插入了密钥,但是当应该检索密钥时程序挂起。以下是我为每个函数实现锁定的方法:

// Inserts a key-value pair into the table
void insert(int key, int val) {
    pthread_mutex_lock(&lock);
    int i = key % NUM_BUCKETS;
    bucket_entry *e = (bucket_entry *) malloc(sizeof(bucket_entry));
    if (!e) panic("No memory to allocate bucket!");
    e->next = table[i];
    e->key = key;
    e->val = val;
    table[i] = e;
    pthread_mutex_unlock(&lock);
    pthread_exit(NULL);
}

// Retrieves an entry from the hash table by key
// Returns NULL if the key isn't found in the table
bucket_entry * retrieve(int key) {
    pthread_mutex_lock(&lock);
    bucket_entry *b;
    for (b = table[key % NUM_BUCKETS]; b != NULL; b = b->next) {
        if (b->key == key) return b;
    }
    pthread_mutex_unlock(&lock);
    pthread_exit(NULL);
    return NULL;
}

所以这里的主要问题是:

  1. 如何判断每个线程操作之间数据丢失的位置

  2. 从哈希表中检索密钥时,什么可能导致程序挂起?

【问题讨论】:

  • 无法知道线程何时会被抢占,这就是你使用锁来保护公共资源的原因。顺便问一下,insert 函数,是全线程函数吗?这就是胎面的全部吗?对于线程来说似乎不是很有用。
  • 如果retrieve() 找到匹配的键,则返回对象而不释放锁。如果它没有找到密钥,它将退出线程。奇怪。
  • 您的程序挂起,因为您在返回成功命中时没有解锁互斥锁。但是为什么你在这两个函数中都执行 pthread_exit 呢?这没有任何意义。

标签: c linux multithreading operating-system pthreads


【解决方案1】:

首先,您应该阅读有关pthreads 的更多信息。另请阅读pthreads(7)。请特别注意,像pthread_mutex_lock 这样的每个锁定调用都应该总是相同互斥体上调用pthread_mutex_unlock(并且通常 > 您应该采用规则,每个锁定和解锁都发生在相同块中)。因此,您的 for 循环中的 return retrieve 是错误的,您应该编码:

bucket_entry * 
retrieve(int key) {
  bucket_entry *res = NULL;
  pthread_mutex_lock(&lock);
  for (bucket_entry *b = table[key % NUM_BUCKETS]; 
       b != NULL; b = b->next) {
     if (b->key == key) 
        { res = b; break; };
  }
  pthread_mutex_unlock(&lock);
  return res;
}

然后您可以使用 valgrind 并使用 recent GCC 编译器(例如,2015 年 11 月的 5.2)。编译所有警告和调试信息 (gcc -Wall -Wextra -g -pthread)。阅读有关消毒剂debugging options 的信息,特别考虑使用-fsanitize=thread

调用pthread_exit 的理由很少(同样,您很少在程序中调用exit)。当你这样做时,整个当前线程将被终止。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-30
    • 2018-02-07
    • 2016-12-28
    • 1970-01-01
    • 2022-08-24
    • 1970-01-01
    相关资源
    最近更新 更多