- r - 以节省内存的方式增长 data.frame
- ruby-on-rails - ruby/ruby on rails 内存泄漏检测
- android - 无法解析导入android.support.v7.app
- UNIX 域套接字与共享内存(映射文件)
我有一个多线程应用程序,它创建了 48 个线程,它们都需要访问一个公共(public)属性 (STL::map)。 map 只会在线程启动时写入,其余时间将从中读取 map 。这似乎是 pthread_rw_lock 的完美用例,而且一切似乎都运行良好。
我遇到了一个完全不相关的段错误并开始分析内核。使用 gdb,我执行了命令 info threads
并且对结果感到非常惊讶。我观察到有几个线程实际上正在按预期从映射中读取,但奇怪的是,有几个线程在等待 rw_lock 的 pthread_rwlock_rdlock() 中被阻塞。
这是等待锁的线程的堆栈跟踪:
#0 0xffffe430 in __kernel_vsyscall ()
#1 0xf76fe159 in __lll_lock_wait () from /lib/libpthread.so.0
#2 0xf76fab5d in pthread_rwlock_rdlock () from /lib/libpthread.so.0
#3 0x0804a81a in DiameterServiceSingleton::getDiameterService(void*) ()
有这么多线程,很难说有多少正在读取,有多少被阻塞,但我不明白为什么任何线程会被阻塞等待读取,考虑到其他线程已经在阅读。
所以这是我的问题:为什么有些线程在等待读取 rw_lock 时被阻塞,而其他线程已经在读取它了?似乎可以同时读取的线程数有限制。
我查看了 pthread_rwlock_attr_t
函数,但没有看到任何相关内容。
操作系统为Linux,SUSE 11。
相关代码如下:
{
pthread_rwlock_init(&serviceMapRwLock_, NULL);
}
// This method is called for each request processed by the threads
Service *ServiceSingleton::getService(void *serviceId)
{
pthread_rwlock_rdlock(&serviceMapRwLock_);
ServiceMapType::const_iterator iter = serviceMap_.find(serviceId);
bool notFound(iter == serviceMap_.end());
pthread_rwlock_unlock(&serviceMapRwLock_);
if(notFound)
{
return NULL;
}
return iter->second;
}
// This method is only called when the app is starting
void ServiceSingleton::addService(void *serviceId, Service *service)
{
pthread_rwlock_wrlock(&serviceMapRwLock_);
serviceMap_[serviceId] = service;
pthread_rwlock_unlock(&serviceMapRwLock_);
}
更新:
正如 MarkB 在评论中提到的,如果我将 pthread_rwlockattr_getkind_np() 设置为优先考虑编写器,并且有一个编写器阻塞等待,那么观察到的行为就有意义了。但是,我使用我认为是优先考虑读者的默认值。我刚刚验证了没有线程阻塞等待写入。我还按照@Shahbaz 在评论中的建议更新了代码并获得了相同的结果。
最佳答案
您只是观察了获取锁所涉及的固有性能问题。这需要一些时间,而你恰好在其中捕获了那些线程。当受锁保护的操作持续时间很短时尤其如此。
Edit: Reading the source,
glibc
useslll_lock
to protect critical sections within its own pthread library data structures. Thepthread_rwlock_rdlock
checks several flags and increments counters, so it does those things while holding a lock. Once those are done, the lock is released withlll_unlock
.
为了演示,我实现了一个在获取 rwlock
后休眠的简短例程。主线程等待他们完成。但在等待之前,它会打印线程实现的并发数。
enum { CONC = 50 };
pthread_rwlock_t rwlock;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
unsigned count;
void *routine(void *arg)
{
int *fds = static_cast<int *>(arg);
pthread_rwlock_rdlock(&rwlock);
pthread_mutex_lock(&mutex);
++count;
if (count == CONC) pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
sleep(5);
pthread_rwlock_unlock(&rwlock);
pthread_t self = pthread_self();
write(fds[1], &self, sizeof(self));
return 0;
}
然后主线程等待计数器达到 50:
int main()
{
int fds[2];
pipe(fds);
pthread_rwlock_init(&rwlock, 0);
pthread_mutex_lock(&mutex);
for (int i = 0; i < CONC; i++) {
pthread_t tid;
pthread_create(&tid, NULL, routine, fds);
}
while (count < CONC) pthread_cond_wait(&cond, &mutex);
pthread_mutex_unlock(&mutex);
std::cout << "count: " << count << std::endl;
for (int i = 0; i < CONC; i++) {
pthread_t tid;
read(fds[0], &tid, sizeof(tid));
pthread_join(tid, 0);
}
pthread_rwlock_destroy(&rwlock);
pthread_exit(0);
}
编辑:使用 C++11 线程支持简化示例:
enum { CONC = 1000 };
std::vector<std::thread> threads;
pthread_rwlock_t rwlock;
std::mutex mutex;
std::condition_variable cond;
unsigned count;
void *routine(int self)
{
pthread_rwlock_rdlock(&rwlock);
{ std::unique_lock<std::mutex> lk(mutex);
if (++count == CONC) cond.notify_one(); }
sleep(5);
pthread_rwlock_unlock(&rwlock);
return 0;
}
int main()
{
pthread_rwlock_init(&rwlock, 0);
{ std::unique_lock<std::mutex> lk(mutex);
for (int i = 0; i < CONC; i++) {
threads.push_back(std::thread(routine, i));
}
cond.wait(lk, [](){return count == CONC;}); }
std::cout << "count: " << count << std::endl;
for (int i = 0; i < CONC; i++) {
threads[i].join();
}
pthread_rwlock_destroy(&rwlock);
pthread_exit(0);
}
关于c++ - pthread_rwlock 可以同时拥有多少个读者?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11866486/
我无法理解为什么第一个读者-作者问题会导致写进程饿死,即:代码如何为读者进程提供优先级?当其中一个读取进程执行 signal(wrt) 时,写入进程是否应该无法获得锁定?是信号量列表的结构(正如我所看
我正在使用R处理人口普查数据,该数据使用了很长的数字GEOID来标识地理位置。我面临的问题是,当使用write_csv(来自readr包)写出处理后的数据时,正在以科学计数法编写这些GEOID。有办法
我为 Java 中的读写器问题开发了一个解决方案(有关此的一些信息 http://lass.cs.umass.edu/~shenoy/courses/fall08/lectures/Lec11.pdf
以下代码是Java中的Passing the Baton程序的一部分: 主要 P1(作家) P2(作家) P3(阅读器) P4(阅读器) 主要(); package ReadersPreference
我正在使用 ReaderWriterLockSlim保护一些操作。我想偏爱读者而不是作者,这样当读者长时间持有锁并且作者试图获取写锁时,进一步的读者不会被作者的尝试阻塞(如果作者在 lock.Ente
我觉得这可能是一种非常普遍和常见的情况,存在众所周知的无锁解决方案。 简而言之,我希望有像读者/作者锁这样的方法,但这不需要读者获取锁,因此可以获得更好的平均性能。 相反,读者需要一些原子操作(128
我遇到了读者-作者问题。我想写出作家喜欢使用互斥锁的解决方案。到目前为止我已经写了这个 #include #include #include #include #include #inclu
这个程序读取“电子邮件”(实际上只是一个像电子邮件一样结构的 .txt 文件)并用它做各种事情(在 map 中存储数据并对其进行操作)。 但是,我在根据主题搜索输出电子邮件的“消息”时遇到了一个不寻常
我正在从 UIWebView 切换到 WKWebView,不知道如何设置我的配置以使用 Reader. 有没有人可以帮助我? viewDidLoad: WKWebViewConfiguration *
我正在练习 Visual Basic 编程我在 Visual Basic 中有两种形式。第一个表单有一个命令按钮,将显示第二个表单的输入数据第二种形式有一个文本框,我需要在其中输入数据并保存它。 我在
我想寻求有关此任务的帮助。 我的任务是用 C 编写一个简单的程序来模拟读写器问题。程序要求是: 程序启动后,会要求用户输入作者和读者的输入次数。 程序会不断通知用户线程的状态。 程序结束后,小统计(每
我有一个用 C 语言模拟读者-作者问题的简单程序。要求用户输入作者数和读者数。然后创建随机数的编写器 - 线程和读取器 - 线程。项目的写入由全局变量 itemsCount 模拟 - 它代表新插入项目
我有一个“静态 64 位整数变量”,它仅由一个线程更新。所有其他线程仅从中读取。 出于安全原因,我是否应该使用原子操作(例如“__sync_add_and_fetch”)来保护这个变量? 还是可以直接
我是一名优秀的程序员,十分优秀!