qsort线程安全吗?
我有一些旧代码,使用 qsort
对 MFC CArray
结构进行排序,但我发现偶尔会发生崩溃,可能是由于多个线程调用同时qsort
。我正在使用的代码看起来像这样:
struct Foo
{
CString str;
time_t t;
Foo(LPCTSTR lpsz, time_t ti) : str(lpsz), t(ti)
{
}
};
class Sorter()
{
public:
static void DoSort();
static int __cdecl SortProc(const void* elem1, const void* elem2);
};
...
void Sorter::DoSort()
{
CArray<Foo*, Foo*> data;
for (int i = 0; i < 100; i++)
{
Foo* foo = new Foo("some string", 12345678);
data.Add(foo);
}
qsort(data.GetData(), data.GetCount(), sizeof(Foo*), SortProc);
...
}
int __cdecl SortProc(const void* elem1, const void* elem2)
{
Foo* foo1 = (Foo*)elem1;
Foo* foo2 = (Foo*)elem2;
// 0xC0000005: Access violation reading location blah here
return (int)(foo1->t - foo2->t);
}
...
Sorter::DoSort();
我即将重构这个可怕的代码以使用 std::sort
代替,但想知道上面的代码是否实际上不安全?
编辑: Sorter::DoSort
实际上是一个静态函数,但本身不使用静态变量。
EDIT2:SortProc 函数已更改以匹配实际代码。
I have some old code that uses qsort
to sort an MFC CArray
of structures but am seeing the occasional crash that may be down to multiple threads calling qsort
at the same time. The code I am using looks something like this:
struct Foo
{
CString str;
time_t t;
Foo(LPCTSTR lpsz, time_t ti) : str(lpsz), t(ti)
{
}
};
class Sorter()
{
public:
static void DoSort();
static int __cdecl SortProc(const void* elem1, const void* elem2);
};
...
void Sorter::DoSort()
{
CArray<Foo*, Foo*> data;
for (int i = 0; i < 100; i++)
{
Foo* foo = new Foo("some string", 12345678);
data.Add(foo);
}
qsort(data.GetData(), data.GetCount(), sizeof(Foo*), SortProc);
...
}
int __cdecl SortProc(const void* elem1, const void* elem2)
{
Foo* foo1 = (Foo*)elem1;
Foo* foo2 = (Foo*)elem2;
// 0xC0000005: Access violation reading location blah here
return (int)(foo1->t - foo2->t);
}
...
Sorter::DoSort();
I am about to refactor this horrible code to use std::sort
instead but wondered if the above is actually unsafe?
EDIT: Sorter::DoSort
is actually a static function but uses no static variables itself.
EDIT2: The SortProc function has been changed to match the real code.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(8)
您的问题不一定与线程安全有任何关系。
排序回调函数接受指向每个项目的指针,而不是项目本身。由于您正在对
Foo*
进行排序,因此您实际想要做的是将参数作为Foo**
访问,如下所示:Your problem doesn't necessarily have anything to do with thread saftey.
The sort callback function takes in pointers to each item, not the item itself. Since you are sorting
Foo*
what you actually want to do is access the parameters asFoo**
, like this:您的 SortProc 没有返回正确的结果,这可能会导致内存损坏,因为假设数据在完成排序后已排序。您甚至可能会在尝试排序时导致 qsort 崩溃,但这当然会因实现而异。
如果第一个对象小于第二个对象,则 qsort 的比较函数必须返回负值;如果它们相等则返回零;否则返回正值。您当前的代码仅返回 0 或 1,而当您应该返回负值时则返回 1。
Your SortProc isn't returning correct results, and this likely leads to memory corruption by something assuming that the data is, well, sorted after you get done sorting it. You could even be leading qsort into corruption as it tries to sort, but that of course varies by implementation.
The comparison function for qsort must return negative if the first object is less than the second, zero if they are equal, and positive otherwise. Your current code only ever returns 0 or 1, and returns 1 when you should be returning negative.
C++ 并没有真正保证线程安全。您最多可以说的是,数据结构的多个读取器或单个写入器都可以。读者和作者的任何组合,您都需要以某种方式序列化访问。
C++ doesn't really make any guarantees about thread safety. About the most you can say is that either multiple readers OR a single writer to a data structure will be OK. Any combination of readers and writers, and you need to serialise the access somehow.
由于您使用
MFC
标记标记了您的问题,我想您应该在项目设置中选择多线程运行时库。Since you tagged your question with
MFC
tag I suppose you should select Multi-threaded Runtime Library in Project Settings.现在,您的代码是线程安全的,但没有用,因为 DoSort 方法仅使用局部变量,甚至不返回任何内容。如果您要排序的数据是 Sorter 的成员,那么从多个线程调用该函数是不安全的。一般来说,请阅读重入,这可能会让您了解什么你需要留意。
Right now, your code is thread-safe, but useless, as the DoSort-method only uses local variables, and doesn't even return anything. If the data you are sorting is a member of Sorter, then it is not safe to call the function from multiple threads. In gerenal, read up on reentrancy, this may give you an idea of what you need to look out for.
使它成为线程安全的是,无论您的对象是否是线程安全的,例如要使 qsort 线程安全,您必须确保向对象写入或读取的任何内容都是线程安全的。
what make it thread safe is, whether your object are thread safe, for example to make qsort thread-safe you must ensure that anything that write or read to or from and to the object are thread safe.
pthreads 手册页列出了不需要线程安全的标准函数。 qsort 不在其中,因此它在 POSIX 中需要是线程安全的。
http://www.kernel.org/ doc/man-pages/online/pages/man7/pthreads.7.html
不过,我找不到 Windows 的等效列表,所以这并不是您问题的真正答案。如果情况有所不同,我会有点惊讶。
不过,请注意在这种情况下“线程安全”的含义。这意味着您可以在不同的数组上同时调用相同的函数——这并不意味着通过 qsort 并发访问相同的数据是安全的(事实并非如此)。
The pthreads man page lists the standard functions which are not required to be thread-safe. qsort is not among them, so it is required to be thread-safe in POSIX.
http://www.kernel.org/doc/man-pages/online/pages/man7/pthreads.7.html
I can't find the equivalent list for Windows, though, so this isn't really an answer to your question. I'd be a bit surprised if it was different.
Be aware what "thread-safe" means in this context, though. It means you can call the same function concurrently on different arrays -- it doesn't mean that concurrent access to the same data via qsort is safe (it isn't).
警告一下,您可能会发现
std::sort
不如qsort
快。如果您确实发现了这一点,请尝试std::stable_sort
。我曾经根据我的 Mark Nelson 在《Dr Dobbs》中提供的代码编写了一个 BWT 压缩器,当我将其转换为类时,我发现常规
排序
要慢得多。stable_sort
修复了速度问题。As a word of warning, you may find
std::sort
is not as fast asqsort
. If you do find that trystd::stable_sort
.I once wrote a BWT compressor based on the code presented my Mark Nelson in Dr Dobbs and when I turned it into classes I found that regular
sort
was a lot slower.stable_sort
fixed the speed problems.