C - 切换上下文并将旧文件存储在队列中

C - Switch Context and Store Old in Queue

提问人:An Assembler 提问时间:11/10/2023 最后编辑:ikegamiAn Assembler 更新时间:11/10/2023 访问量:48

问:

我正在尝试使用该库在 c 中实现线程切换器。ucontext.h

我使用队列系统执行此操作,其中要运行的下一个线程位于队列的前面。当线程暂停时;我弹出队列顶部的节点,将正在运行的上下文与存储在该节点中的上下文切换,然后将该节点推回队列。这看起来像这样:

我有一个测试用例,我在队列中放置了两个线程。每个线程在暂停之前打印其 ID。我希望两者交替出现。相反,第一个线程运行到完成,第二个线程不运行。我已将此问题缩小到交换线程的方式:

QManager.c网站

void pause () {
    struct ThreadNode* t= QManagerPop(Q);
    swapcontext(t->context, t->context);
    QManagerPush(t);
}

看似;这不会将正在运行的上下文与存储在“t->context”中的上下文切换。

我正在寻找一种方法来做到这一点;将当前上下文放在变量“t->context”中,同时恢复存储在该变量中的上下文。

我根据在网上找到的一些信息尝试了以下方法

QManager.c网站

void switch (ucontext_t* c) {
    volatile int isSwap = 0;
    int isGet = getcontext(c);
    if (!isSwap && isGet ==0) {
        isSwap = 1;
        setcontext(c);
    }
}

void pause () {
    struct ThreadNode* t= QManagerPop(Q);
    switch(t->context);
    QManagerPush(t);
}

这将产生相同的行为(末尾有一个额外的段错误)。

有没有办法做我想做的事情?也许是带有临时变量的东西?当上下文切换时,我将如何处理此变量?

C 多线程 ucontext

评论

1赞 ikegami 11/10/2023
您不小心为两个参数传递了相同的指针。swapcontext
0赞 An Assembler 11/10/2023
这并非偶然。我想在 t->context 中加载上下文,并将之前的上下文保存在 t->context 中。它不起作用,但我不知道正确的方法。
0赞 An Assembler 11/10/2023
是的,这是错误的。这就是我的代码不起作用的原因。我根本不知道做我想做的事的正确方法。我问了这个问题,希望能找到答案,并将我的错误代码放在问题中,以便人们可以看到我到底做了什么不起作用。
0赞 ikegami 11/10/2023
您似乎也希望立即返回,以便您可以在其他任何事情发生之前致电。但只有在您切换回您切换的上下文时才会返回,您没有将其保存在任何地方。swapcontextQManagerPush(t)swapcontext
0赞 ikegami 11/10/2023
回复“这不是偶然的”,嗯,参数是 ,并且两次传递相同的指针会违反这一点。回复“我不知道正确的方法”,好吧,对于初学者来说,你需要不止一个。restrictucontext_t

答:

0赞 An Assembler 11/10/2023 #1

好吧,我解决了我的问题。感谢@ikegami指出我不能使用相同的参数调用 swapcontext,也不能期望函数在交换后推送。我的代码现在看起来像

void pause () {
    struct ThreadNode* t= QManagerPop(Q);
    struct ThreadNode* s= malloc(sizeof(struct ThreadNode));
    s -> context = malloc(sizeof(ucontext_t));
    QManagerPush(s);
    swapcontext(QManagerTail(Q)->context, t->context);
}

评论

0赞 ikegami 11/10/2023
你永远不会自由和.tt->context
0赞 ikegami 11/10/2023 #2
  • 上下文的两个参数不能是同一个指针,因为参数是 .swapcontextrestrict
  • 交换后,无法将旧上下文推送到队列中。该“线程”不再执行!它需要在交换之前完成。

您可以使用线程 ID,而不是分配和取消分配所有这些线程,也就是说,索引到包含线程的上下文和其他数据的结构数组中。

然后,切换上下文上下文变为

void switch_to_thread( ThreadId new_thread_id ) {
   ThreadId old_thread_id = current_thread_id;
   current_thread_id = new_thread_id;
   swapcontext(
      &( threads[ old_thread_id ].context ),
      &( threads[ new_thread_id ].context )
   );
}

并成为pause

void yield( void ) {
   QManagerEnqueue( q, current_thread_id );
   ThreadId new_thread_id = QManagerDequeue( q );
   switch_to_thread( new_thread_id );
}

实际上,如果所有线程都在一个数组中,那么我们就不需要队列。我们只需要找到数组中的下一个线程。

ThreadId get_next_thread( void ) {
   ThreadId thread_id = current_thread_id;
   while ( 1 ) {
      thread_id = ( thread_id + 1 ) % MAX_THREADS;

      int state = threads[ thread_id ].state;
      if ( state == STATE_CREATED || state == STATE_RUNNING )
         return thread_id;
   }
}

void yield( void ) {
   switch_to_thread( get_next_thread() );
}

这段代码来自我之前的回答,您可以在其中看到这些函数的使用。


请注意以下修复:

  • void f()并不意味着该函数不带参数。

请注意以下重命名:

  • Q⇒ .
    它不是模块名称或常量。
    q
  • QManagerPush⇒和
    ⇒。
    Push 和 pop 是堆栈操作,而不是队列操作。
    QManagerEnqueueQManagerPopQManagerDequeue
  • pause⇒ .
    或者是更合适的名称。
    yieldyieldcede