增量同步原语

发布于 2024-10-28 20:45:37 字数 4821 浏览 3 评论 0原文

我是一个研究线程的初学者, 我有一个作业来解决 os161 的互斥问题,通过启动几个递增公共计数器的线程来从 0 计数到 10000。我不知道如何使用同步原语来改进它,请帮助。

#include <types.h>
#include <lib.h>
#include <test.h>
#include <thread.h>
#include <synch.h>

enum {
    NADDERS = 10,    /* the number of adder threads */
    NADDS   = 10000, /* the number of overall increments to perform */
};

/*
 * **********************************************************************
 * Declare the counter variable that all the adder() threads increment 
 *
 * Declaring it "volatile" instructs the compiler to always (re)read the
 * variable from memory and not optimise by removing memory references
 * and re-using the content of a register.
 */
volatile unsigned long int counter;


/*
 * Declare an array of adder counters to count per-thread
 * increments. These are used for printing statistics.
 */  
unsigned long int adder_counters[NADDERS];


/* We use a semaphore to wait for adder() threads to finish */
struct semaphore *finished;

/*
 * **********************************************************************
 * ADD YOUR OWN VARIABLES HERE AS NEEDED
 * **********************************************************************
 */

/*
 * adder()
 *
 *  Each adder thread simply keeps incrementing the counter until we
 *  hit the max value.
 *
 * **********************************************************************
 * YOU NEED TO INSERT SYNCHRONISATION PRIMITIVES APPROPRIATELY 
 * TO ENSURE COUNTING IS CORRECTLY PERFORMED.
 * **********************************************************************
 *
 * You should not re-write the existing code.
 *
 * * Only the correct number of increments are performed
 * * Ensure x+1 == x+1 
 * * Ensure that the statistics kept match the number of increments
 * * performed.
 *
 *
 */

static void adder(void * unusedpointer, unsigned long addernumber)
{
    unsigned long int a, b;
    int flag = 1;

    /*
     * Avoid unused variable warnings.
     */
    (void) unusedpointer; /* remove this line if variable is used */

    while (flag) {
        /* loop doing increments until we achieve the overall number
           of increments */

        a = counter;

        if (a < NADDS) {

            counter = counter + 1;

            b = counter;

            /* count the number of increments we perform  for statistics */
            adder_counters[addernumber]++;    

            /* check we are getting sane results */
            if (a + 1 != b) {
                kprintf("In thread %ld, %ld + 1 == %ld?\n", addernumber, a, b) ;
            }
        }
        else {
            flag = 0;
        }
    }

    /* signal the main thread we have finished and then exit */
    V(finished);

    thread_exit();
}

/*
 * math()
 *
 * This function:
 *
 * * Initialises the counter variables
 * * Creates a semaphore to wait for adder threads to complete
 * * Starts the define number of adder threads
 * * waits, prints statistics, cleans up, and exits
 */
int maths (int nargs, char ** args)
{
    int index, error;
    unsigned long int sum;

    /*
     * Avoid unused variable warnings.
     */

    (void) nargs;
    (void) args;

    /* create a semaphore to allow main thread to wait on workers */

    finished = sem_create("finished", 0);

    if (finished == NULL) {
        panic("maths: sem create failed");
    }

    /*
     * **********************************************************************
     * INSERT ANY INITIALISATION CODE YOU REQUIRE HERE
     * **********************************************************************
     */


    /*
     * Start NADDERS adder() threads.
     */


    kprintf("Starting %d adder threads\n", NADDERS);

    for (index = 0; index < NADDERS; index++) {

        error = thread_fork("adder thread", &adder, NULL, index, NULL);

        /*
         * panic() on error.
         */

        if (error) {
            panic("adder: thread_fork failed: %s\n", strerror(error));
        }
    }


    /* Wait until the adder threads complete */

    for (index = 0; index < NADDERS; index++) {
        P(finished);
    }

    kprintf("Adder threads performed %ld adds\n", counter);

    /* Print out some statistics */
    sum = 0;
    for (index = 0; index < NADDERS; index++) {
        sum += adder_counters[index];
        kprintf("Adder %d performed %ld increments.\n", 
                index, adder_counters[index]);
    }
    kprintf("The adders performed %ld increments overall\n", sum);

    /*
     * **********************************************************************
     * INSERT ANY CLEANUP CODE YOU REQUIRE HERE 
     * **********************************************************************
     */


    /* clean up the semaphore we allocated earlier */
    sem_destroy(finished);
    return 0;
}

i am a begineer studying threads,
i have a homework to solve a mutual exclusion problem with os161, to counts from 0 to 10000 by starting several threads that increment a common counter. i have no ideas how to use synchronisation primitives to improve it, please help.

#include <types.h>
#include <lib.h>
#include <test.h>
#include <thread.h>
#include <synch.h>

enum {
    NADDERS = 10,    /* the number of adder threads */
    NADDS   = 10000, /* the number of overall increments to perform */
};

/*
 * **********************************************************************
 * Declare the counter variable that all the adder() threads increment 
 *
 * Declaring it "volatile" instructs the compiler to always (re)read the
 * variable from memory and not optimise by removing memory references
 * and re-using the content of a register.
 */
volatile unsigned long int counter;


/*
 * Declare an array of adder counters to count per-thread
 * increments. These are used for printing statistics.
 */  
unsigned long int adder_counters[NADDERS];


/* We use a semaphore to wait for adder() threads to finish */
struct semaphore *finished;

/*
 * **********************************************************************
 * ADD YOUR OWN VARIABLES HERE AS NEEDED
 * **********************************************************************
 */

/*
 * adder()
 *
 *  Each adder thread simply keeps incrementing the counter until we
 *  hit the max value.
 *
 * **********************************************************************
 * YOU NEED TO INSERT SYNCHRONISATION PRIMITIVES APPROPRIATELY 
 * TO ENSURE COUNTING IS CORRECTLY PERFORMED.
 * **********************************************************************
 *
 * You should not re-write the existing code.
 *
 * * Only the correct number of increments are performed
 * * Ensure x+1 == x+1 
 * * Ensure that the statistics kept match the number of increments
 * * performed.
 *
 *
 */

static void adder(void * unusedpointer, unsigned long addernumber)
{
    unsigned long int a, b;
    int flag = 1;

    /*
     * Avoid unused variable warnings.
     */
    (void) unusedpointer; /* remove this line if variable is used */

    while (flag) {
        /* loop doing increments until we achieve the overall number
           of increments */

        a = counter;

        if (a < NADDS) {

            counter = counter + 1;

            b = counter;

            /* count the number of increments we perform  for statistics */
            adder_counters[addernumber]++;    

            /* check we are getting sane results */
            if (a + 1 != b) {
                kprintf("In thread %ld, %ld + 1 == %ld?\n", addernumber, a, b) ;
            }
        }
        else {
            flag = 0;
        }
    }

    /* signal the main thread we have finished and then exit */
    V(finished);

    thread_exit();
}

/*
 * math()
 *
 * This function:
 *
 * * Initialises the counter variables
 * * Creates a semaphore to wait for adder threads to complete
 * * Starts the define number of adder threads
 * * waits, prints statistics, cleans up, and exits
 */
int maths (int nargs, char ** args)
{
    int index, error;
    unsigned long int sum;

    /*
     * Avoid unused variable warnings.
     */

    (void) nargs;
    (void) args;

    /* create a semaphore to allow main thread to wait on workers */

    finished = sem_create("finished", 0);

    if (finished == NULL) {
        panic("maths: sem create failed");
    }

    /*
     * **********************************************************************
     * INSERT ANY INITIALISATION CODE YOU REQUIRE HERE
     * **********************************************************************
     */


    /*
     * Start NADDERS adder() threads.
     */


    kprintf("Starting %d adder threads\n", NADDERS);

    for (index = 0; index < NADDERS; index++) {

        error = thread_fork("adder thread", &adder, NULL, index, NULL);

        /*
         * panic() on error.
         */

        if (error) {
            panic("adder: thread_fork failed: %s\n", strerror(error));
        }
    }


    /* Wait until the adder threads complete */

    for (index = 0; index < NADDERS; index++) {
        P(finished);
    }

    kprintf("Adder threads performed %ld adds\n", counter);

    /* Print out some statistics */
    sum = 0;
    for (index = 0; index < NADDERS; index++) {
        sum += adder_counters[index];
        kprintf("Adder %d performed %ld increments.\n", 
                index, adder_counters[index]);
    }
    kprintf("The adders performed %ld increments overall\n", sum);

    /*
     * **********************************************************************
     * INSERT ANY CLEANUP CODE YOU REQUIRE HERE 
     * **********************************************************************
     */


    /* clean up the semaphore we allocated earlier */
    sem_destroy(finished);
    return 0;
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

ζ澈沫 2024-11-04 20:45:37

由于您是该领域的初学者,因此不要使用花哨的东西。只需通过互斥体保护您的计数器即可。

// with static linkage somewhere
pthread_mutex_t countMut = PTHREAD_MUTEX_INITIALIZER;
size_t count = 0;

// in the functions
pthread_mutex_lock(&countMut);
++count;
pthread_mutex_unlock(&countMut);

Since you are a beginner in that domain, don't use the fancy stuff. Just protect your counter by a mutex and that is it.

// with static linkage somewhere
pthread_mutex_t countMut = PTHREAD_MUTEX_INITIALIZER;
size_t count = 0;

// in the functions
pthread_mutex_lock(&countMut);
++count;
pthread_mutex_unlock(&countMut);
猫腻 2024-11-04 20:45:37

一些小事;

  1. 不要对这些值使用枚举 - 使用定义 - 枚举用于类似类型的事物,例如水果、错误类型等。线程数和增量数不同.

  2. 易失性不会对任何事情产生任何影响 - 它只是指示编译器永远不要优化读取;但你是在递增,所以你总是要在写入之前读取变量

但你是在递增,所以你总是要在写入And 到主要问题

  1. ;最简单的解决方案是互锁增量; GCC 指令提供了这样的功能

A couple of minor things;

  1. don't use an enum for those values - use defines - an enum is for things of a similar type, e.g. fruits, error types, etc. Number of threads and number of increments are different.

  2. volatile won't make any difference to anything - it merely instructs the compiler never to optimize reads away; but you're incrementing, so you're always going to read the variable before writing

And to the main question;

  1. the easiest solution to this is an interlocked increment; the GCC instrincs offer such a function
别想她 2024-11-04 20:45:37

请注意,如果计数器位于内存中并标记为“易失性”,则“计数器 = 计数器 + 1”不是原子操作。因此,加一操作必须受到某种互斥体的保护。

您可以使用 os161 的锁定功能来保护线程之间的共享数据。代码可能是这样的:

// declare a global lock variable so every threads can access it
static struct lock* counter_lock;

// initialize the lock before you fork threads
counter_lock = lock_create("counter lock");

// when each thread tries to access the counter, use lock to protect it
lock_acquire(counter_lock);
counter++;
lock_release(counter_lock);

// destroy the lock after all threads are done
lock_destroy(counter_lock);

当然,作为作业,你必须自己实现/kern/thread/synch.c 中的这些锁接口。

Note that "counter = counter + 1" is not a atomic operation if counter lies in memory and marked as "volatile". So the add-by-one operation must be protected by some kind of mutex thing.

You can use os161's lock facility to protect the shared data among threads. The code may look like this:

// declare a global lock variable so every threads can access it
static struct lock* counter_lock;

// initialize the lock before you fork threads
counter_lock = lock_create("counter lock");

// when each thread tries to access the counter, use lock to protect it
lock_acquire(counter_lock);
counter++;
lock_release(counter_lock);

// destroy the lock after all threads are done
lock_destroy(counter_lock);

Of course, as a assignment, you must implement thess lock interfaces in /kern/thread/synch.c by yourself.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文