我有一个相当大的动态稀疏矩阵对象类要编写,我希望发生以下情况:一个线程处理将元素放入矩阵,另一个处理从矩阵读取。
这两者冲突的唯一时间是他们想要同时访问同一行/列。因此,我已经确定每个行/列的简单互斥锁就足够了。
现在这是我第一次在C / C ++中实际进行线程化,我想通过书籍来做这件事,可以这么说。我有两个问题。
答案 0 :(得分:18)
如果这是您第一次进行多线程,请使用Boost.Threads库。它的语义(包括同步机制)非常简单,您的实现也是可移植的。
答案 1 :(得分:6)
C ++本身不提供任何线程。在Windows上,您可以使用CreateThread
。在UNIX上,您可以使用POSIX线程(pthreads)。
不应该实现自己的并发原语。例如,在Windows上,您可以使用CreateMutex
创建互斥对象,并使用WaitForSingleObject
等待它被释放。
答案 2 :(得分:3)
首先,每列不需要互斥锁,每行不需要一个互斥锁。如果为某行获取了互斥锁,则会锁定该行中的所有单元格,因此访问哪个列无关紧要。或者,如果每列获得一个锁,则锁定该列中的所有单元格,无论哪一行都无关紧要。因此,每个表可以有一个互斥锁,每个单元一个,每行一个或每列一个。但是每行和每列一个是没有意义的。
大多数同步原语会阻塞你的线程,线程只会在资源空闲时恢复,你不需要担心信号和唤醒。那部分正是像互斥锁或关键部分这样的同步对象为你做的事情。
如何构造和使用同步原语的细节是特定于平台的。正如其他人发布的那样,您可以使用跨平台库,但您必须至少指定目标平台,以便我们了解可用的库。
答案 3 :(得分:1)
我可以相当简单地回答第一部分 - 这取决于你的平台。如果您使用的是Win32 API,请查看http://msdn.microsoft.com/en-us/library/ms682453%28VS.85%29.aspx“CreateThread”函数并阅读示例。我在Windows上阅读多线程的那本书是这样的:http://www.amazon.co.uk/Windows-PRO-Developer-Jeffrey-Wintellect-Christophe/dp/0735624240不仅包括使用CreateThread的线程,还包括其他选项BeginThread,还包括锁和semaphones等。
如果你正在使用Linux,你需要通过pthread函数获得POSIX线程,请参阅http://www.yolinux.com/TUTORIALS/LinuxTutorialPosixThreads.html作为示例。
pthreads的代码示例如下所示 - 请注意,我已经保留了从同一函数创建多个线程的功能,即调用一个pthread_t变量数组。你可能不需要这个。
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <malloc.h>
void *thread_function(void *arg)
{
/* DO SOME STUFF */
/* Exit Thread */
pthread_exit();
}
int main(int argc, char** argv)
{
/* variables */
int retval = 0;
/* array of thread handles */
pthread_t* thread_handle = (pthread_t*) calloc(1, sizeof(pthread_t));;
/* create function - fork() for threads */
retval = pthread_create(&thread_handle[0], NULL, thread_function, NULL);
/* DO SOME STUFF */
/* join - wait for thread to finish */
pthread_join(thread_handle[0], NULL);
return EXIT_SUCCESS;
}
使用gcc filename -o fileexe -lpthread
答案 4 :(得分:1)
你确定要一个矩阵,你所描述的听起来像一个队列。锁定队列非常简单:当任何人读或写时,他们会进行独占锁定(通过pthread_mutex)。 (除非你真的知道你遇到了麻烦,否则不要进入读写锁等)
当然不需要中断
答案 5 :(得分:1)
答案 6 :(得分:1)
我要警告你关于锁定的事情是小心不要死锁。您希望按行和列锁定,因此每行和每列都需要自己的互斥锁。 (实际上,reader/writer lock会更好地表现,但你必须更加小心死锁和竞争条件。)
确保始终以一致的顺序获取和释放锁定;你不希望一个线程锁定行N然后阻止锁定列K,然后锁定列K的线程决定锁定行N和块,给你两个被阻塞的线程并且没有任何事情发生(提示John Woo手枪对峙)
答案 7 :(得分:1)
快速了解,选择std::threads的可用实现之一,然后考虑std::async和std::future
及相关工具。
答案 8 :(得分:1)
关于你的第二个问题,关于如何进行锁定:让线程进入休眠状态并将其唤醒将由操作系统完成,这不是你的担心。但是你的方案存在问题。
您希望仅在其行及其列被锁定时才阻止对单元格的访问。也就是说,如果行或列已解锁,则允许访问。这通常不是锁的工作方式。此外,如果该行被锁定但您仍允许访问(因为该列已解锁),您仍然希望“更多”锁定它。这意味着您需要的不仅仅是互斥锁。
我能想到的最佳实现是对行使用一个原子计数器,为列使用条件变量计数器。访问时:
这涉及一些花哨的步法,但总锁定资源总数相当低。其他人可以做得更好(或找到我的计划的反例)吗?
另请注意,将矩阵划分为行和列有点随意。如果在此方案下发生过多争用,您可能应该将行细分为一半(例如)。
答案 9 :(得分:0)
如果每个行/列都有一个互斥锁,当行写入线程和列读取线程访问行/列交叉点处的元素时,您将遇到竞争条件。如果你有一个大矩阵,你需要很多互斥。我不确定每个操作系统都能提供数千个互斥锁。
听起来像线程安全的生产者 - 消费者队列可以更好地解决您的问题。查看英特尔的Thread Building Blocks(TBB)库。我发现使用数据流范例为程序建模时,多线程变得更加容易。
我想减少大型sparce矩阵的内存占用量,请查看Boost.uBlas。它有一个sparce_matrix
模板类,它使用map来按索引关联存储元素。
出于效率原因,您不希望通过复制将整个矩阵传递给生产者 - 消费者队列。相反,你会想要传递你的sparce矩阵的代理。如果TBB还没有某种代理工具,你可以简单地使用boost :: shared_ptr。您可能还希望在数据流“电路”中使用预先分配的矩阵池。
答案 10 :(得分:0)
不是让单独的线程进行读写(总是需要锁定),你可以限制线程只访问矩阵的特定元素,比如一半行的单个线程和后一半的其他线程(或者一个线程用于偶数)行和一行奇数行)这样你可以确保没有线程被阻止