qsort线程安全吗?

发布于 2024-08-24 13:20:57 字数 1055 浏览 4 评论 0原文

我有一些旧代码,使用 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

狼亦尘 2024-08-31 13:20:57

您的问题不一定与线程安全有任何关系。

排序回调函数接受指向每个项目的指针,而不是项目本身。由于您正在对 Foo* 进行排序,因此您实际想要做的是将参数作为 Foo** 访问,如下所示:

int __cdecl SortProc(const void* elem1, const void* elem2)
{
  Foo* foo1 = *(Foo**)elem1;
  Foo* foo2 = *(Foo**)elem2;
  if(foo1->t < foo2->t) return -1;
  else if (foo1->t > foo2->t) return 1;
  else return 0;
}

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 as Foo**, like this:

int __cdecl SortProc(const void* elem1, const void* elem2)
{
  Foo* foo1 = *(Foo**)elem1;
  Foo* foo2 = *(Foo**)elem2;
  if(foo1->t < foo2->t) return -1;
  else if (foo1->t > foo2->t) return 1;
  else return 0;
}
分開簡單 2024-08-31 13:20:57

您的 SortProc 没有返回正确的结果,这可能会导致内存损坏,因为假设数据在完成排序后已排序。您甚至可能会在尝试排序时导致 qsort 崩溃,但这当然会因实现而异。

如果第一个对象小于第二个对象,则 qsort 的比较函数必须返回负值;如果它们相等则返回零;否则返回正值。您当前的代码仅返回 0 或 1,而当您应该返回负值时则返回 1。

int __cdecl Sorter::SortProc(const void* ap, const void* bp) {
  Foo const& a = *(Foo const*)ap;
  Foo const& b = *(Foo const*)bp;
  if (a.t == b.t) return 0;
  return (a.t < b.t) ? -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.

int __cdecl Sorter::SortProc(const void* ap, const void* bp) {
  Foo const& a = *(Foo const*)ap;
  Foo const& b = *(Foo const*)bp;
  if (a.t == b.t) return 0;
  return (a.t < b.t) ? -1 : 1;
}
宛菡 2024-08-31 13:20:57

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.

梦里南柯 2024-08-31 13:20:57

由于您使用 MFC 标记标记了您的问题,我想您应该在项目设置中选择多线程运行时库。

Since you tagged your question with MFC tag I suppose you should select Multi-threaded Runtime Library in Project Settings.

梦屿孤独相伴 2024-08-31 13:20:57

现在,您的代码是线程安全的,但没有用,因为 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.

鸠书 2024-08-31 13:20:57

使它成为线程安全的是,无论您的对象是否是线程安全的,例如要使 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.

命比纸薄 2024-08-31 13:20:57

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).

虚拟世界 2024-08-31 13:20:57

警告一下,您可能会发现 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 as qsort. If you do find that try std::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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文