天天看點

ProtoThreads的搶占式排程問題

Protothreads是Adam Dunkels實作的使用者級輕量級線程庫。它的首頁在: http://dunkels.com/adam/pt/index.html。

它使用特殊技巧的switch語句模拟了使用者線程的排程原語。可以使用的使用者線程數不受限制。由于簡易,Protothreads線程不支援局部變量,需要避開使用者的switch語句,以及不支援搶占式排程。

這裡設想通過添加計數器的方法,模拟時間片,時間片到時之後,強制釋放處理器,模拟通常的搶占式排程。

pt.h中, struct pt增加兩個變量:

int count;

int reload;

PT_BEGIN宏,裡面增加一句:

…; (pt)->count= -(pt)->reload; …

再增加2個宏:

#define PT_INIT_COUNT(pt, n) ((pt)->reload=n)

#define PT_COUNT(pt) if(++(pt)->count>=0) PT_YIELD(pt)

現在來看效果。利用原來的example-small.c例子,其中加入了 PT_COUNT,為了便于觀察,主程式中再添加一句列印:

printf(“Sched on %d\n”, ++i);

/**
 * This is a very small example that shows how to use
 * protothreads. The program consists of two protothreads that wait
 * for each other to toggle a variable.
 */
~
/* We must always include pt.h in our protothreads code. */
#include "pt.h"
~
#include <stdio.h> /* For printf(). */

/* Two flags that the two protothread functions use. */
static int protothread1_flag, protothread2_flag;

/**
 * The first protothread function. A protothread function must always
 * return an integer, but must never explicitly return - returning is
 * performed inside the protothread statements.
 *
 * The protothread function is driven by the main loop further down in
 * the code.
 */
static int
protothread1(struct pt *pt)
{
static int i;
  /* A protothread function must begin with PT_BEGIN() which takes a
     pointer to a struct pt. */
  PT_BEGIN(pt);

  /* We loop forever here. */
  while(1) {
    /* Wait until the other protothread has set its flag. */
    PT_WAIT_UNTIL(pt, protothread2_flag != 0);
    printf("Protothread 1 running\n");

        for(i=0;i<20; i++) {
                printf("%d.\n", i);
                PT_COUNT(pt);
        }
    /* We then reset the other protothread's flag, and set our own
       flag so that the other protothread can run. */
    protothread2_flag = 0;
    protothread1_flag = 1;

    /* And we loop. */
  }

  /* All protothread functions must end with PT_END() which takes a
     pointer to a struct pt. */
  PT_END(pt);
}

/**
 * The second protothread function. This is almost the same as the
 * first one.
 */
static int
protothread2(struct pt *pt)
{
  PT_BEGIN(pt);

  while(1) {
    /* Let the other protothread run. */
    protothread2_flag = 1;

    /* Wait until the other protothread has set its flag. */
    PT_WAIT_UNTIL(pt, protothread1_flag != 0);
    printf("Protothread 2 running\n");

    /* We then reset the other protothread's flag. */
    protothread1_flag = 0;

    /* And we loop. */
  }
  PT_END(pt);
}

/**
 * Finally, we have the main loop. Here is where the protothreads are
 * initialized and scheduled. First, however, we define the
 * protothread state variables pt1 and pt2, which hold the state of
 * the two protothreads.
 */
static struct pt pt1, pt2;
int
main(void)
{
        int i=0;
  /* Initialize the protothread state variables with PT_INIT(). */
  PT_INIT(&pt1); PT_INIT_COUNT(&pt1, 5);
  PT_INIT(&pt2); PT_INIT_COUNT(&pt2, 5);

  /*
   * Then we schedule the two protothreads by repeatedly calling their
   * protothread functions and passing a pointer to the protothread
   * state variables as arguments.
   */
  while(1) {
        printf("Sched on %d.\n", ++i);
    protothread1(&pt1);
    protothread2(&pt2);
  }
}

           

編譯。運作。這裡摘錄了運作結果的一個片斷:

Sched on 181.
Protothread 2 running
Sched on 182.
Protothread 1 running
0.
1.
2.
3.
4.
Sched on 183.
5.
6.
7.
8.
9.
Sched on 184.
10.
11.
12.
13.
14.
Sched on 185.
15.
16.
17.
18.
19.
Sched on 186.
Protothread 2 running
Sched on 187.
Protothread 1 running
0.
1.
2.
3.
4.
Sched on 188.
           

可以看到,線上程一for循環模拟的耗時操作過程中,發生了多次排程。

繼續閱讀