Linux线程暂停/恢复

时间:2012-07-13 10:05:12

标签: c linux multithreading pthreads

我正在编写一个代码,其中我有两个并行运行的线程。

1st是启动第二个线程的主线程。 第二个线程只是一个执行空循环的简单线程。

现在我想暂停/暂停创建它的第一个线程执行第二个线程。 过了一段时间后,我想从暂停/暂停的位置恢复执行第二个线程(通过发出一些命令或功能)。

9 个答案:

答案 0 :(得分:8)

这个问题不是关于如何使用互斥锁,而是如何挂起线程。

在Unix规范中,有一个名为pthread_suspend的线程函数,另一个名为pthread_resume_np,但由于某种原因,制作Linux,FreeBSD,NetBSD等的人没有实现这些功能。

所以要理解它,功能根本就不存在。有一些解决方法但不幸的是它与在Windows上调用SuspendThread不同。你必须做各种非便携式的东西才能使线程停止并开始使用信号。

停止和恢复线程对调试器和垃圾收集器至关重要。例如,我看过Wine的一个版本无法正确实现“SuspendThread”功能。因此,任何使用它的Windows程序都无法正常工作。

我认为有可能使用基于JVM使用这种信号技术为垃圾收集器这一事实的信号正确地做到这一点,但我也刚刚在网上看到一些人们注意到死锁的文章,等等。 JVM,有时候是不可复制的。

所以要回答这个问题,除非你有一个很好的Unix来实现pthread_suspend_np,否则你无法在Unix中正确挂起和恢复线程。否则你会被信号困住。

信号的一个大问题是当你有大约五个不同的库都链接到同一个程序并且所有库都试图同时使用相同的信号时。出于这个原因,我相信你实际上不能在一个程序中使用像ValGrind这样的东西,例如Boehm GC。至少在最低级别的用户空间没有主要编码。

这个问题的另一个答案可能是。做Linuz Torvalds对NVidia所做的事情,转过头来指责他并让他实现Linux中缺少的两个最关键的部分。首先是pthread_suspend,其次是内存页面上的脏位,以便可以实现正确的垃圾收集器。在线开始大量请愿并继续翻转手指。也许在Windows 20发布时,他们会意识到暂停和恢复线程,并且有脏位实际上是Windows和Mac比Linux更好的基本原因之一,或者任何没有实现pthread_suspend的Unix以及脏位在虚拟页面上,如VirtualAlloc在Windows中。

我不生活在希望之中。实际上对我来说,我花了很多年时间计划我的未来,为Linux构建东西,但已经放弃了希望作为一个可靠的东西似乎都取决于虚拟内存的脏位的可用性,以及干净地挂起线程。

答案 1 :(得分:4)

据我所知,你不能只是使用pthreads暂停其他一些线程。你必须在你的第二个线程中有一些东西检查它应该使用像条件变量之类的东西暂停。这是做这类事情的标准方法。

答案 2 :(得分:1)

POSIX中没有pthread_suspend(),pthread_resume()类API。
大多数条件变量可用于控制其他线程的执行。

  

条件变量机制允许线程暂停执行   并放弃处理器直到某些条件为真。一个条件   变量必须始终与互斥锁相关联以避免竞争   一个线程准备等待和另一个线程创建的条件   这可能在第一个线程实际等待之前发出信号   在它上面造成了僵局。

了解更多信息

Pthreads

Linux Tutorial Posix Threads

答案 3 :(得分:1)

如果您可以使用进程,则可以将作业控制信号(SIGSTOP / SIGCONT)发送到第二个进程。如果您仍想在这些进程之间共享内存,可以使用SysV共享内存(shmop,shmget,shmctl ...)。

即使我自己没有尝试过,也许可以使用较低级别的clone()系统调用来生成不共享信号的线程。有了它,您可以将SIGSTOP和SIGCONT发送到另一个线程。

答案 4 :(得分:0)

您可以使用互斥锁来执行此操作,伪代码将为:

While (true) {
    /* pause resume */
    lock(my_lock); /* if this is locked by thread1, thread2 will wait until thread1 */
                   /* unlocks it */
    unlock(my_lock); /* unlock so that next iteration thread2 could lock */

    /* do actual work here */
}

答案 5 :(得分:0)

不确定您是否愿意接听我的答案。但你可以这样做。

如果是一个单独的流程而非线程我有一个解决方案(这可能适用于线程,也许有人可以分享您的想法)使用信号

当前没有系统暂停或恢复进程的执行。 但你肯定可以建立一个。

如果我想在项目中使用它,我会做的步骤:

  • 为第二个进程注册信号处理程序。

  • 在信号处理程序中,等待信号量。

  • 每当您想要暂停其他过程时,只需发送信号
    您注册了其他进程。该计划将进入 睡眠状态。

  • 如果要恢复此过程,可以发送不同的信号 再次。在信号处理程序内部,您将检查信号量是否为 锁定与否。如果它被锁定,您将释放信号量。所以
    流程2将继续执行。

如果您可以实现此功能,请分享您的feedack,如果它适合您。感谢。

答案 6 :(得分:0)

为了在线程上实现暂停,您需要让它等待某个事件发生。等待自旋锁互斥是CPU周期浪费。恕我直言,不应该遵循这种方法,因为其他进程/线程可能已经耗尽了CPU周期。 等待非阻塞描述符(管道,套接字或其他)。可以在此处看到使用pipes for inter-thread communication的示例代码 如果您的第二个线程具有来自多个源的更多信息,而不仅仅是暂停和恢复信号,则上述解决方案很有用。可以在非阻塞描述符上使用顶级选择/轮询/ epoll。您可以指定select / poll / epoll系统调用的等待时间,并且仅浪费那么多微秒的CPU周期。 我提出这个解决方案,前瞻性地认为你的第二个线程将有更多的事情或事件要处理,而不仅仅是暂停和恢复。对不起,如果它比你问的更详细。

另一种更简单的方法是在这些线程之间拥有共享的布尔变量。 主线程是变量的编写者,0 - 表示停止。 1 - 表示简历 第二个线程只读取变量的值。要实现'0'状态,请使用usleep进行sime微秒,然后再次检查该值。假设您的设计中几乎没有微秒延迟。 要实现'1' - 在执行一定数量的操作后检查变量的值。 否则,您还可以实现从“1”状态转换为“0”状态的信号。

答案 7 :(得分:0)

您可以通过信号暂停线程

pthread_mutex_t mutex;
static void thread_control_handler(int n, siginfo_t* siginfo, void* sigcontext) {
    // wait time out
    pthread_mutex_lock(&mutex);
    pthread_mutex_unlock(&mutex);
}
// suspend a thread for some time
void thread_suspend(int tid, int time) {
    struct sigaction act;
    struct sigaction oact;
    memset(&act, 0, sizeof(act));
    act.sa_sigaction = thread_control_handler;
    act.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK;
    sigemptyset(&act.sa_mask);
    pthread_mutex_init(&mutex, 0);
    if (!sigaction(SIGURG, &act, &oact)) {
        pthread_mutex_lock(&mutex);
        kill(tid, SIGURG);
        sleep(time);
        pthread_mutex_unlock(&mutex);
    }
}

答案 8 :(得分:0)

我尝试使用信号挂起和恢复线程,这是我的解决方案。请编译并链接-pthread。

信号SIGUSR1通过调用pause()挂起线程,而SIGUSR2恢复线程。

在暂停的手册页中:

  

pause()使调用进程(或线程)进入睡眠状态,直到   传递的信号会终止该过程或导致   调用          信号捕捉功能。

#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <signal.h>

// Since I have only 2 threads so using two variables, 
// array of bools will be more useful for `n` number of threads.
static int is_th1_ready = 0;
static int is_th2_ready = 0;

static void cb_sig(int signal)
{
        switch(signal) {
        case SIGUSR1:
                pause();
                break;
        case SIGUSR2:
                break;
        }
}

static void *thread_job(void *t_id)
{
        int i = 0;
        struct sigaction act;

        pthread_detach(pthread_self());
        sigemptyset(&act.sa_mask);
        act.sa_flags = 0;
        act.sa_handler = cb_sig;

        if (sigaction(SIGUSR1, &act, NULL) == -1) 
                printf("unable to handle siguser1\n");
        if (sigaction(SIGUSR2, &act, NULL) == -1) 
                printf("unable to handle siguser2\n");

        if (t_id == (void *)1)
            is_th1_ready = 1;
        if (t_id == (void *)2)
            is_th2_ready = 1;

        while (1) {
                printf("thread id: %p, counter: %d\n", t_id, i++);
                sleep(1);
        }

        return NULL;
}

int main()
{
        int terminate = 0;
        int user_input;
        pthread_t thread1, thread2;

        pthread_create(&thread1, NULL, thread_job, (void *)1);
        // Spawned thread2 just to make sure it isn't suspended/paused 
        // when thread1 received SIGUSR1/SIGUSR2 signal
        pthread_create(&thread2, NULL, thread_job, (void *)2);

        while (!is_th1_ready && !is_th2_ready);

        while (!terminate) {
                // to test, I am sensing signals depending on input from STDIN
                printf("0: pause thread1, 1: resume thread1, -1: exit\n");
                scanf("%d", &user_input);

                switch(user_input) {
                case -1: 
                        printf("terminating\n");
                        terminate = 1;
                        break;
                case 0:
                        printf("raising SIGUSR1 to thread1\n");
                        pthread_kill(thread1, SIGUSR1);
                        break;
                case 1:
                        printf("raising SIGUSR2 to thread1\n");
                        pthread_kill(thread1, SIGUSR2);
                        break;
                }
        }

        pthread_kill(thread1, SIGKILL);
        pthread_kill(thread2, SIGKILL);

        return 0;
}