Windows 有ctrl+;中断阻塞系统调用

Windows 有ctrl+;中断阻塞系统调用,windows,blocking,thread-sleep,interrupt-handling,keyboardinterrupt,Windows,Blocking,Thread Sleep,Interrupt Handling,Keyboardinterrupt,下面是我正在使用的代码的精简示例: void SleepIntr() { printf("Entering sleep...\n"); SleepEx(10000, TRUE); printf("Sleep done...\n"); } static BOOL WINAPI ctrl_handler(DWORD dwCtrlType) { printf("ctrl_handler pressed\n"); // Wake program up! r

下面是我正在使用的代码的精简示例:

void SleepIntr()
{
    printf("Entering sleep...\n");
    SleepEx(10000, TRUE);
    printf("Sleep done...\n");
}
static BOOL WINAPI ctrl_handler(DWORD dwCtrlType) {
    printf("ctrl_handler pressed\n");
    // Wake program up!
    return TRUE;
}
int _tmain(int argc, _TCHAR* argv[])
{
    SetConsoleCtrlHandler(ctrl_handler, TRUE);
    SleepIntr();
    printf("awake!\n"); 
    return 0;
}
这个想法是当按下ctr+c时,睡眠应该被中断,程序应该继续执行并打印“awake!”,或者至少给我一个当按下ctr+c时执行位置的堆栈跟踪

SleepEx
调用可以是任何阻塞的系统调用,例如从套接字读取的阻塞

我应该在我的
ctrl\u处理程序中写些什么来中断呼叫?除了结束整个过程,我什么也没发现。如果这是不可能的,那么我会接受这个答案,但我真的想知道为什么

下面是为Linux编写的等效程序,使用
nanosleep
而不是
SleepEx

#include <signal.h>
#include <stdio.h>
#include <time.h>
void ctrl_c (int sid, siginfo_t *info, void *data) {
    printf("Im a ctrl_c\n");
}
int main (int argc, char *argv[]) {
    struct sigaction act = {0};
    act.sa_sigaction = &ctrl_c;
    sigaction(SIGINT, &act, NULL);
    struct timespec t, t2;
    t.tv_sec = 5;
    t.tv_nsec = 0;
    printf("sleep start\n");
    nanosleep (&t, &t2);
    printf("sleep end\n");
    return 0;
}
#包括
#包括
#包括
void ctrl_c(int-sid,siginfo_t*info,void*data){
printf(“ima ctrl_c\n”);
}
int main(int argc,char*argv[]){
struct sigaction act={0};
act.sa_sigaction=&ctrl\u c;
sigation(SIGINT,&act,NULL);
结构时间段t,t2;
t、 tv_sec=5;
t、 tv_nsec=0;
printf(“睡眠开始\n”);
纳米睡眠(t&t和t2);
printf(“睡眠结束\n”);
返回0;
}
它的工作原理和我想要的一模一样。nanosleep中的Ctrl+c立即中断它,并通过打印“睡眠结束”继续执行。为什么类似的东西不能在Windows上为SleepEx工作


EDITSleepEx
的第二个参数当然应该为TRUE。这是一个输入错误。

无法回避这样一个事实,即您需要知道您正在中断什么,并且代码必须设计为被中断

在您选择的特定示例中,将睡眠替换为事件的定时等待。然后可以通过设置事件来中断它

如果进行同步I/O调用,可以使用
CancelSynchronousIo
,但处理I/O的代码当然必须正确处理取消

如果您处于可警报等待状态,则可以将APC排入线程队列

如果您处于消息循环中,则可以发布消息或让循环使用
MsgWaitForMultipleObjectsEx

附加:演示如何唤醒的演示代码
SleepEx

#include <Windows.h>
#include <stdio.h>
#include <tchar.h>

HANDLE mainthread;

VOID CALLBACK DummyAPCProc(
  _In_  ULONG_PTR dwParam
)
{
    printf("User APC\n");
    return;
}

void SleepIntr()
{
    printf("Entering sleep...\n");
    SleepEx(10000, TRUE);
    printf("Sleep done...\n");
}

static BOOL WINAPI ctrl_handler(DWORD dwCtrlType) {
    printf("ctrl_handler pressed\n");
    // Wake program up!
    if (!QueueUserAPC(DummyAPCProc, mainthread, NULL))
    {
        printf("QueueUserAPC: %u\n", GetLastError());
        return TRUE;
    }
    return TRUE;
}

int _tmain(int argc, _TCHAR* argv[])
{
    if (!DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), &mainthread, GENERIC_ALL, FALSE, 0))
    {
        printf("DuplicateHandle: %u\n", GetLastError());
        return 0;
    }

    SetConsoleCtrlHandler(ctrl_handler, TRUE);
    SleepIntr();
    printf("awake!\n"); 
    return 0;
}   
#包括
#包括
#包括
处理主线程;
无效回调dummyapproc(
_In_ULONG_PTR dwParam
)
{
printf(“用户APC\n”);
返回;
}
void SleepIntr()
{
printf(“进入睡眠…\n”);
SleepEx(10000,真实);
printf(“睡眠完成…\n”);
}
静态BOOL WINAPI ctrl\u处理程序(DWORD dwCtrlType){
printf(“按下ctrl\u处理程序\n”);
//醒醒!
如果(!QueueUserAPC(DummyAPCProc,主线程,NULL))
{
printf(“QueueUserAPC:%u\n”,GetLastError());
返回TRUE;
}
返回TRUE;
}
int _tmain(int argc,_TCHAR*argv[]
{
if(!DuplicateHandle(GetCurrentProcess(),GetCurrentThread(),GetCurrentProcess(),&mainthread,泛型,FALSE,0))
{
printf(“重复句柄:%u\n”,GetLastError());
返回0;
}
setConsoletrlHandler(ctrl\u handler,TRUE);
SleepIntr();
printf(“唤醒!\n”);
返回0;
}   

这不是一个选项。你有5秒钟的时间处理回叫。如果在此之前不终止程序,Windows将为您执行此操作。只要您愿意,就可以在gui程序中随意忽略关闭请求。编写可安全中止的代码是程序员的工作,当然,首先不要让线程休眠10秒钟。您可以通过关闭句柄来解除I/O呼叫的阻止。
nanosleep
专门用于在收到信号时退出,类似于APC排队时退出的方式,前提是您将
bAlertable
设置为
TRUE
。唯一的区别是,在Windows中,程序员可以选择睡眠是否可中断。当然,在Linux中,程序员总是可以在循环中调用
nanosleep
,检查时间是否已过。这相当于将
bAlertable
设置为
FALSE
,只是不太方便:而且它会阻止您同样有效地中断代码。我正在编写一个虚拟机,它应该能够很好地处理行为不正常的代码(不挂起),因此用更好的函数替换SleepEx并不总是一个选项。我认为我能做的最好的组合是
CancelSynchronousIO
QueueUserAPC
?我不明白你想达到什么目的。如果行为不端的代码是(;;)的
,该怎么办?那我当然不走运了。然而,这种不正常的行为并不像卡在套接字或类似设备上的阻塞读取那样常见。这正是我希望能够中断的事情。好吧,
CancelSynchronousIo
可以做到这一点——如果您知道要将哪个线程作为目标线程的话——但很可能代码不知道如何响应被终止的I/O,并且会导致崩溃、带错误消息退出进程,或者处于通常不起作用的状态。至于可提醒的等待,它们应该总是在循环中调用,所以您可能无法做太多事情。这没关系。阻塞函数是使用一种字节码调用的,因此只要该函数返回,vm就可以重新获得控制并干净地继续。很难解释清楚,但所谓的SleepEx和friends根本不是C代码,而是因素。然而,我现在已经用
CancelSynchronousIO
QueueUserAPC
做了很多实验,它们都没有真正中断
SleepEx
调用。至少不是我例子中的那个。