【问题标题】:Handling two threads, one sleeping and one waiting for input处理两个线程,一个在休眠,一个在等待输入
【发布时间】:2018-05-14 03:59:21
【问题描述】:

问题:创建一个接受用户输入但几秒钟后超时的程序(假设现在是 2 秒)。

方法:我创建了两个线程,一个等待用户输入(inputThreadtid[0]),另一个休眠 2 秒(sleepThreadtid[1])。我从另一个线程的例程中取消一个线程,如下:

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

pthread_t tid[2];

void* inputThread()
{   
    int n;
    printf("Enter number:");

    // wait for input; if input is given then go ahead and cancel the sleeper thread.
    scanf("%d",&n); 

    // cancel the sleeper thread
    pthread_cancel(tid[1]); 

    printf("Got value:%d\n",n);
}

void* sleepThread()
{
    // sleep for 2 seconds and cancel the input thread.
    sleep(2); 

    // cancel the input thread
    pthread_cancel(tid[0]); 

    printf("\nNo value entered!\n");
}

int main(int argc, char const *argv[])
{
    int r1,r2,r3,r4;

    // input taking thread 
    r1 = pthread_create(&tid[0],NULL,inputThread,NULL); 

    // sleeping thread
    r2 = pthread_create(&tid[1],NULL,sleepThread,NULL);

    r3 = pthread_join(tid[0],NULL);
    r4 = pthread_join(tid[1],NULL);

    return 0;
}

截至目前,该程序按预期运行。

但我的朋友说它不能保证工作,因为它取决于线程的调度方式。他试图向我解释同样的事情,但我无法理解。他还说pthread_cancel只是一个取消线程的请求,它可能不会成功。

所以有人可以指出潜在的错误和避免相同的最佳做法。为保证程序正常工作而进行的任何更改也值得赞赏。

【问题讨论】:

    标签: c multithreading pthreads


    【解决方案1】:

    关于pthread_cancel 是取消线程的请求并且可能不会总是成功的部分,你的朋友是对的。

    关于调度的部分,你需要提供他有什么论据。

    为了解决您的问题,您可以放弃一起使用线程(除非这是您的任务的要求)并使用selectread

    这将使解决方案不可移植(仅符合 POSIX),但pthreads 无论如何也不可移植。

    select 允许您等待文件描述符(在本例中为 0)等待数据可供读取。它还允许您设置超时。

    fd_set set;
    FD_ZERO(&set);
    FD_SET(0, &set);
    
    struct timeval timeout;
    timeout.tv_sec = 2;
    timeout.tv_usec = 0;
    
    int ret = select(1, &set, NULL, NULL, &timeout);
    
    if (ret == 1) {
        char buffer[20];
        int n;
        read(0, buffer, 20);
        sscanf(buffer, "%d", &n);
        printf("Got value:%d\n",n);
    } else {
        printf("Time out\n");
    }
    

    【讨论】:

    • 感谢您的解决方案。使用线程不是必需的,但我们使用线程只是为了学习线程库。如果只使用线程,我该如何处理?是否只能在一个线程中?
    • @RahulBharadwaj 我给出的解决方案是使用单线程。如果你想使用多个线程来做,正确的方法是有一个睡眠线程(就像你现在一样)。超时后,它将volatile 标志设置为0。主线程将stdin (0) fd 设置为非阻塞模式并继续尝试读取(适当的睡眠时间为10 毫秒)。在每次重复之前检查 volatile 变量。这样您就可以获得相同的行为。
    • 好的,我明白了。除了main 线程之外,我将尝试使用一个额外的线程来实现。
    • @RahulBharadwaj 您可以查看this 以了解如何将 0 fd 设置为非阻塞模式。虽然我会建议,dup 首先是 fd,然后再继续阅读。如果 0 fd 设置为非阻塞,我不确定标准库代码(如 scanf、gets)会如何表现。
    • 如果用户不按回车,显示的代码不会超时。
    【解决方案2】:

    你的朋友是对的,我认为他的意思是你不能假设知道哪个线程首先运行/完成。 但肯定会在 2 秒内安排两个线程,因此 thread1 有时间询问用户输入并稍等片刻。

    我没有发现大问题,如果没问题,请不要太担心。

    而不是sleep,但是您可以使用 SIGALRM 信号,但它涉及掩码,并且需要更多时间来理解(我正在做this project:如果需要示例,请检查task2 函数和它使用的掩码)。

    【讨论】:

    • 我用过 sigprocmasksignal 。从来没有想过,会尝试使用它来实现它,谢谢!
    【解决方案3】:

    pthread_cancel 函数调用线程上的线程取消处理程序,然后,一旦它们返回,将调用线程的析构函数,进而终止线程。

    因为是请求,所以可能会失败,这由pthread_cancel返回一个非零整数表示。

    如果您使用两个以上的线程来执行此操作,或者有多个线程在同一进程中访问标准输入,您可能会得到线程等待访问标准输入,从而导致所需的行为被延迟。

    【讨论】:

    • 只有一个线程在访问stdin。您可以建议pthread_cancel 的任何替代方案吗?
    • @RahulBharadwaj 是的,您可以使用pthread_exit,它作为 void 的返回类型并且永远不会失败。
    • @JoshWeinstein pthread_exit,杀死调用线程。因此,它必须从等待线程中调用,这是不可能的。没有可靠的方法可以从另一个线程中杀死一个线程。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-04-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多