在没有内核支持的情况下唤醒线程

发布于 2024-12-02 00:46:10 字数 106 浏览 0 评论 0原文

是否有任何机制可以让我在不通过内核的情况下唤醒另一个进程中的线程?等待线程可能会循环旋转,没问题(每个线程都固定到一个单独的核心),但在我的情况下,发送线程必须很快,并且不能通过内核来唤醒等待线程。

Is there any mechanism through which I can wake up a thread in another process without going through the kernel? The waiting thread might spin in a loop, no problem (each thread is pegged to a separate core), but in my case the sending thread has to be quick, and can't afford to go through the kernel to wake up the waiting thread.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

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

发布评论

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

评论(3

少年亿悲伤 2024-12-09 00:46:10

不,如果另一个线程正在休眠(不在 CPU 上)。要唤醒这样的线程,您需要通过调用属于内核一部分的调度程序将其状态更改为“RUNNING”。

是的,如果两个线程或进程运行在不同的 CPU 上,并且它们之间存在共享内存,则可以同步两个线程或进程。您应该将所有线程绑定到不同的 CPU。然后你可以使用 spinlock:pthread_spin_lockpthread_spin_unlock 来自 POSIX Pthread 可选部分的函数 ('(ADVANCED REALTIME THREADS)'; [THR SPI]);或任何自定义自旋锁。自定义自旋锁很可能会使用一些原子操作和/或内存屏障。

发送线程将更改内存中的值,该值由接收器线程循环检查。

例如

init:

pthread_spinlock_t lock;
pthread_spin_lock(&lock);  // close the "mutex"

然后启动线程。

等待线程:

{
pthread_spin_lock(&lock); // wait for event;
work();
}

主线程:

{
do_smth();
pthread_spin_unlock(&lock); // open the mutex; other thread will see this change
 //  in ~150 CPU ticks (checked on Pentium4 and Intel Core2 single socket systems);
 // time of the operation itself is of the same order; didn't measure it.
continue_work();
}

No, if the other thread is sleeping (not on CPU). To wake up such thread you need to change its state into "RUNNING" by calling scheduler which is part of the kernel.

Yes, you can syncronize two threads or processes if both are running on different CPUs, and if there is shared memory between them. You should bind all threads to different CPUs. Then you may use spinlock:pthread_spin_lock and pthread_spin_unlock functions from optional part of POSIX's Pthread ('(ADVANCED REALTIME THREADS)'; [THR SPI]); or any of custom spinlock. Custom spinlock most likely will use some atomic operations and/or memory barriers.

Sending thread will change the value in memory, which is checked in loop by receiver thread.

E.g.

init:

pthread_spinlock_t lock;
pthread_spin_lock(&lock);  // close the "mutex"

then start threads.

waiting thread:

{
pthread_spin_lock(&lock); // wait for event;
work();
}

main thread:

{
do_smth();
pthread_spin_unlock(&lock); // open the mutex; other thread will see this change
 //  in ~150 CPU ticks (checked on Pentium4 and Intel Core2 single socket systems);
 // time of the operation itself is of the same order; didn't measure it.
continue_work();
}
埋葬我深情 2024-12-09 00:46:10

为了向另一个进程发出信号表明它应该继续,而不强迫发送者在内核调用上花费时间,我们立即想到了一种机制。如果没有内核调用,进程所能做的就是修改内存;所以解决方案是进程间共享内存。一旦发送方写入共享内存,接收方应该在没有任何显式内核调用的情况下看到更改,并且接收方的简单轮询应该可以正常工作。

一种廉价(但可能不够便宜)的替代方案是将发送委托给同一进程中的辅助线程,并让辅助线程进行适当的进程间“信号量释放”或管道写入调用。

To signal to another process that it should continue, without forcing the sender to spend time in a kernel call, one mechanism comes to mind right away. Without kernel calls, all a process can do is modify memory; so the solution is inter-process shared memory. Once the sender writes to shared memory, the receiver should see the change without any explicit kernel calls, and naive polling by the receiver should work fine.

One cheap (but maybe not cheap enough) alternative is delegating the sending to a helper thread in the same process, and have the helper thread make a proper inter-process "semaphore release" or pipe write call.

嘿咻 2024-12-09 00:46:10

我知道您希望避免使用内核以避免与内核相关的开销。大多数此类开销与上下文切换相关。下面演示了一种使用信号来完成所需任务的方法,无需旋转,也无需上下文切换:

#include <signal.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <pthread.h>
#include <iostream>
#include <thread>

using namespace std;

void sigRtHandler(int sig) {
    cout << "Recevied signal" << endl;
}

int main() {
    constexpr static int kIter = 100000;
    thread t([]() {
        signal(SIGRTMIN, sigRtHandler);
        for (int i = 0; i < kIter; ++i) {
            usleep(1000);
        }
        cout << "Done" << endl;
    });
    usleep(1000);   // Give child time to setup signal handler.
    auto handle = t.native_handle();
    for (int i = 0; i < kIter; ++i)
        pthread_kill(handle, SIGRTMIN);
    t.join();
    return 0;
}

如果运行此代码,您将看到子线程不断接收 SIGRTMIN。当进程运行时,如果您查看该进程的文件 /proc/(PID)/task/*/status,您将看到父线程不会因调用 pthread_kill() 而导致上下文切换。

这种方法的优点是等待线程不需要自旋。如果等待线程的作业对时间不敏感,则此方法可以让您节省 CPU。

I understand that you want to avoid using the kernel in order to avoid kernel-related overheads. Most of such overheads are context-switch related. Here is a demonstration of one way to accomplish what you need using signals without spinning, and without context switches:

#include <signal.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <pthread.h>
#include <iostream>
#include <thread>

using namespace std;

void sigRtHandler(int sig) {
    cout << "Recevied signal" << endl;
}

int main() {
    constexpr static int kIter = 100000;
    thread t([]() {
        signal(SIGRTMIN, sigRtHandler);
        for (int i = 0; i < kIter; ++i) {
            usleep(1000);
        }
        cout << "Done" << endl;
    });
    usleep(1000);   // Give child time to setup signal handler.
    auto handle = t.native_handle();
    for (int i = 0; i < kIter; ++i)
        pthread_kill(handle, SIGRTMIN);
    t.join();
    return 0;
}

If you run this code, you'll see that the child thread keeps receiving the SIGRTMIN. While the process is running, if you look in the files /proc/(PID)/task/*/status for this process, you'll see that parent thread does not incur context switches from calling pthread_kill().

The advantage of this approach is that the waiting thread doesn't need to spin. If the waiting thread's job is not time-sensitive, this approach allows you to save CPU.

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