【问题标题】:pthread condition variables vs win32 events (linux vs windows-ce)pthread 条件变量 vs win32 事件(linux vs windows-ce)
【发布时间】:2012-01-16 17:03:13
【问题描述】:

我正在一个 arm imx27 板上进行 Windows CE 和 Linux 之间的性能评估。该代码已经为 CE 编写,并测量了执行不同内核调用所需的时间,例如使用互斥锁和信号量等操作系统原语、打开和关闭文件以及网络。

在将此应用程序移植到 Linux (pthreads) 期间,我偶然发现了一个我无法解释的问题。几乎所有测试都显示性能从 5 倍提高到 10 倍,但不是我的 win32 events 版本(SetEventWaitForSingleObject),CE 实际上“赢得”了这个测试。

为了模拟我使用 pthreads 条件变量的行为(我知道我的实现并没有完全模拟 CE 版本,但对于评估来说已经足够了)

测试代码使用两个线程,它们使用事件相互“乒乓”。


Windows 代码:

线程 1:(我测量的线程)

HANDLE hEvt1, hEvt2;
hEvt1 = CreateEvent(NULL, FALSE, FALSE, TEXT("MyLocEvt1"));
hEvt2 = CreateEvent(NULL, FALSE, FALSE, TEXT("MyLocEvt2"));

ResetEvent(hEvt1);
ResetEvent(hEvt2);

for (i = 0; i < 10000; i++)
{
    SetEvent (hEvt1);
    WaitForSingleObject(hEvt2, INFINITE);
}        

线程 2:(只是“响应”)

while (1)
{
    WaitForSingleObject(hEvt1, INFINITE);
    SetEvent(hEvt2);
}

Linux 代码:

线程 1:(我测量的线程)

struct event_flag *event1, *event2;
event1 = eventflag_create();
event2 = eventflag_create();

for (i = 0; i < 10000; i++)
{
    eventflag_set(event1);
    eventflag_wait(event2);
}

线程 2:(只是“响应”)

while (1)
{
    eventflag_wait(event1);
    eventflag_set(event2);
}

我对@9​​87654331@的实现:

struct event_flag* eventflag_create()
{
    struct event_flag* ev;
    ev = (struct event_flag*) malloc(sizeof(struct event_flag));

    pthread_mutex_init(&ev->mutex, NULL);
    pthread_cond_init(&ev->condition, NULL);
    ev->flag = 0;

    return ev;
}

void eventflag_wait(struct event_flag* ev)
{
    pthread_mutex_lock(&ev->mutex);

    while (!ev->flag)
        pthread_cond_wait(&ev->condition, &ev->mutex);

    ev->flag = 0;

    pthread_mutex_unlock(&ev->mutex);
}

void eventflag_set(struct event_flag* ev)
{
    pthread_mutex_lock(&ev->mutex);

    ev->flag = 1;
    pthread_cond_signal(&ev->condition);

    pthread_mutex_unlock(&ev->mutex);
}

还有struct

struct event_flag
{
    pthread_mutex_t mutex;
    pthread_cond_t  condition;
    unsigned int    flag;
};

问题:

  • 为什么我在这里看不到性能提升?
  • 可以采取哪些措施来提高性能(例如,是否有更快的方法来实施 CE 行为)?
  • 我不习惯编写 pthread 编码,我的实现中是否存在可能导致性能损失的错误?
  • 是否有任何替代库可以解决这个问题?

【问题讨论】:

  • 你可以检查这个实现 stackoverflow.com/questions/178114/… 。购买不要期望性能有任何提高。 Linux simple 本身不支持事件之类的东西,仿真永远不会这么快。这与 Windows 中的条件变量相同。旧的 windows 版本不支持它,模拟也相当复杂。
  • @Zuljin:那里的实现与我的版本几乎相同。 :-(
  • 不能用其他原语实现吗?
  • 我所做或看到的所有测试都让 CE 轻松赢得同步对象,所以你的结果是我所期望的。但是,您可能会发现 Linux 的网络堆栈比 CE 快得多(我最后一次测试肯定是这样)。

标签: windows linux events pthreads windows-ce


【解决方案1】:

请注意,在调用 pthread_cond_signal() 时您不需要持有互斥锁,因此您可以通过在发出条件信号之前释放互斥锁来提高条件变量“事件”实现的性能:

void eventflag_set(struct event_flag* ev)
{
    pthread_mutex_lock(&ev->mutex);

    ev->flag = 1;

    pthread_mutex_unlock(&ev->mutex);

    pthread_cond_signal(&ev->condition);
}

这可能会阻止被唤醒的线程立即阻塞互斥体。

【讨论】:

  • 不应该是先 cond_signal 后是 mutex_unlock 吗?通过将它们按此顺序排列,您在执行 cond_signal 之前留下一些其他参与者锁定互斥锁的可能性很小,这样信号等待者在收到信号后不会自动锁定互斥锁。
【解决方案2】:

这种类型的实现只有在您能够承受错过某个事件的情况下才有效。我刚刚对其进行了测试并遇到了许多死锁。主要原因是条件变量只唤醒了一个已经在等待的线程。之前发出的信号丢失了。

如果条件已经发出信号,则没有计数器与允许等待线程继续继续的条件相关联。 Windows 事件支持这种类型的使用。

我想不出比使用初始化为零的信号量(POSIX 版本非常易于使用)更好的解决方案,使用sem_post() 代表set()sem_wait() 代表wait()。您肯定可以想出一种方法,使用 sem_getvalue() 将信号量计数设为最大 1

也就是说,我不知道 POSIX 信号量是否只是 Linux 信号量的简洁接口,或者性能损失是什么。

【讨论】:

  • 这就是为什么条件变量总是与...条件相关联的原因。您使用互斥锁来确保对该条件的一致访问,并在 while 循环中检查它。 (以考虑虚假唤醒,以及并发互斥锁获取可能在通知后更改条件的线程。)
  • 我要解决的问题是发生一次的事件。想象一个调度其他线程的线程。例如,您希望仅当分派的线程实际运行了一段时间时才继续执行分派器。为此,我使用了两个信号量:调度程序创建线程并发布一个“调度”信号量。分派的线程等待它。在设置调度信号量之后,调度程序等待工作人员发布一个工作人员信号量。使用上述事件,这种机制会失败,因为在大多数情况下,worker 会错过调度程序的信号并永远阻塞。
  • 当然是的。这就是为什么条件变量不关注事件,而是关注条件。你的条件是一个变量,例如一个标记“去吧!”的布尔值你的工人会读到的。如果它是假的,它会在 condition_variable 上等待,直到一个信号,它被唤醒,检查'go for it flag'。如果标志为真,则意味着信号是在工人到达该点之前发送的,因此它甚至不会等待。互斥体在这里使用正确的内存屏障访问此标志。
猜你喜欢
  • 1970-01-01
  • 2011-08-15
  • 2011-03-02
  • 1970-01-01
  • 2013-09-02
  • 1970-01-01
  • 2012-10-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多