【问题标题】:Working with POSIX Threads: Program hangs every fourth or fifth run, but otherwise seems to work使用 POSIX 线程:程序每运行四次或五次就会挂起,但其他情况下似乎可以工作
【发布时间】:2017-10-27 03:47:56
【问题描述】:

我有一个 C 程序,我必须编写它来创建 4 个线程。这 4 个“海盗”线程应该从“洞穴”(价值 1000 的双倍)中获取 1000 颗珍珠,从“洞穴”中取出 10% 或 15%。他们应该一次只去一个,当珍珠洞空了时,程序应该停止。该程序似乎运行正常,但在运行几次可执行文件后挂起。

为什么会这样?

代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <math.h>

#define NUM_PIRATE_THREADS 4
#define OPEN_SESAME 0
#define OPEN_WATERMELON 1

/* array of threads */
static pthread_t *thread_handles;

/* mutual exlusion lock; mutual exclusion considered in this version */
pthread_mutex_t mutex;

/* pirate thread function */
void *pirate(void* rank);

/* total number of items in resource (pearls in the cave) */
static double cavePearls = 1000.00;

/* amount of pearls pirate thread may take */
static double pearlsToGrab;

/* array to store the number of pearls garnered by each pirate */
int piratesBooty[NUM_PIRATE_THREADS];

/* main function */
int main() {

    /* variable to loop through threads and output pearl consumption */
    long threadIndex;

    /* thread variable */
    long currentThread;

    /* initialize thread_handles array */
    thread_handles = (pthread_t*)malloc(NUM_PIRATE_THREADS*sizeof(pthread_t));

    /* create pirate threads...ARGGG!*/
    for (currentThread = 0; currentThread < NUM_PIRATE_THREADS; ++currentThread) {
        pthread_create(&thread_handles[currentThread], NULL, (void *(*)(void *))pirate, (void*)currentThread);
    }

    /* join pirate threads...ARGGG!*/
    for (currentThread = 0; currentThread < NUM_PIRATE_THREADS; ++currentThread) {
        pthread_join(thread_handles[currentThread], NULL);
    }

    /* update your final cave pearl number to a whole integer value */
    cavePearls = ceil(cavePearls);

    /* display pearl data after thread(s) execution */
    printf("\nPearls left in cave: %d\n", (int)cavePearls);
    for (threadIndex = 0; threadIndex < NUM_PIRATE_THREADS; ++threadIndex){
        printf("Pirate %ld: %d pearls\n", threadIndex, piratesBooty[threadIndex]);
    }

    /* free memory */
    free(thread_handles);

    return 0;
}

void *pirate(void* rank) {

    while(1) { /* continue execution as long as there are still pearls in the cave */
        if (cavePearls == 0) /* cave has been emptied, pirates should stop */
            return 0;

        /* identify which pirate thread you are executing */
        long my_rank = (long)rank;

        /* CRITICAL SECTION LOCKED */
        pthread_mutex_lock(&mutex);

        if (cavePearls) {
            if (my_rank % 2 == OPEN_SESAME) { /* if thread is 0 or 2: "Open, sesame" pirate */
                pearlsToGrab = cavePearls * 0.10; /* "open sesame" = 10% of pearls removed */
                pearlsToGrab = ceil(pearlsToGrab); /* get the ceiling if not an integer value */
                cavePearls = cavePearls - pearlsToGrab; /* update the number of pearls in cave */
                piratesBooty[my_rank] += pearlsToGrab; /* update pirate thread total pearls taken */

            }
            else if (my_rank % 2 == OPEN_WATERMELON){ /* if thread is 1 or 3: "Open, watermelon" pirate */
                pearlsToGrab = cavePearls * 0.15; /* "open watermelon" = 15% of pearls removed */
                pearlsToGrab = ceil(pearlsToGrab); /* get the ceiling if not an integer value */
                cavePearls = cavePearls - pearlsToGrab; /* update the number of pearls in cave */
                piratesBooty[my_rank] += pearlsToGrab; /* update pirate thread total pearls taken */
            }
        } /* end of while-loop */

        /* CRITICAL SECTION UNLOCKED */
        pthread_mutex_unlock(&mutex);

        /* DEBUG SCRIPT
         printf("I am thread %ld, I have %d pearls, I'm taking %d pearls, and \nthere are %d pearls left"
         "in the cave.\n\n", my_rank, piratesBooty[my_rank], (int)pearlsToGrab, (int)cavePearls);
         */

    }
    /* have thread(s) terminate */
    pthread_exit((void*)0);
}

一切看起来都很好!进行了更改,事情看起来不错。如果您发现任何错误,请随时告诉我!

代码:

/*******************************************************************************
 *******************************************************************************
 Course: Operating Systems
 Student: Douglas Adolph
 Project: 3, Part 2

 -------------------------------------------------------------------------------
 ///////////////////////////////////////////////////////////////////////////////
 IMPORTANT NOTE:
 ///////////////////////////////////////////////////////////////////////////////
 Some of this code borrows directly from material provided by Dr. Burtscher
 in Texas State University's Parallel Programming course, as well as material
 written by the author (Douglas Adolph) for said parallel programming course.
 ///////////////////////////////////////////////////////////////////////////////
 -------------------------------------------------------------------------------

 -------------------------------------------------------------------------------
 ///////////////////////////////////////////////////////////////////////////////
 SUMMARY:
 ///////////////////////////////////////////////////////////////////////////////
 Program illustrates use of threads and mutual exclusion. N "pirates" (threads)
 complete for 1000 pearls in a cave. Passwords are used to be granted a certain
 percentage of the remaining pearls in the cave. Program should terminate when
 all the pearls have been taken.
 ///////////////////////////////////////////////////////////////////////////////
 -------------------------------------------------------------------------------

 -------------------------------------------------------------------------------
 ///////////////////////////////////////////////////////////////////////////////
 PART 1 AND 2 SPECIFICS:
 ///////////////////////////////////////////////////////////////////////////////
 Part 1: implementation with mutual exclusion NOT considered*
 Part 2: implementation with mutual exclusion considered

 *designed to fail during repeated runtime tests: demonstrate need for exclusion
 *sleep() not used
 ///////////////////////////////////////////////////////////////////////////////
 -------------------------------------------------------------------------------

 -------------------------------------------------------------------------------
 ///////////////////////////////////////////////////////////////////////////////
 THREAD ID, THREAD PASSWORD, AND RESOURCE (PEARLS) RETRIEVAL INFORMATION:
 ///////////////////////////////////////////////////////////////////////////////
 Pirate threads ID'd by rank (i.e. 0, 1, 2, ...), and this rank is used to ID
 which password pirate thread will use. However, in the program output, pirate
 threads are labeled alphanumerically (i.e. A, B, C, ...). However, although
 pirate threads in output are labeled alphanumerically to match professor's
 example, some liberty was taken in the final design of the output.

 Note: if there are more than 26 pirate threads, the implementation of the output
       of total pearls gathered by all pirate threads will need to be changed, or
       labels for all pirate threads after Z will not have reasonable labels

 "Open, sesame:" N/2 pirate processes use this password and
                 receive 10% of the remaining pearls

 "Open, watermelon:" N/2 pirate processes use this password and
                     receive 15% of the remaining pearls

 Even-numbered pirate threads will be assigned password "Open, sesame," where
 password string is represented by 1 (*)

 Odd-numbered pirate threads will be assigned password "Open, watermelon," where
 password string is represented by 0 (*)

 (*) via a modulus operation (thread % 2); string PWs/buffers avoided
 ///////////////////////////////////////////////////////////////////////////////
 -------------------------------------------------------------------------------

 -------------------------------------------------------------------------------
 ///////////////////////////////////////////////////////////////////////////////
 NOTES REGARDING TRANSFER AND EXECUTION OF FILE ON TEXAS STATE SERVERS:
 ///////////////////////////////////////////////////////////////////////////////

 (1) transfer to Zeus for testing effected via:

 scp /Users/douglasadolph/Desktop/School/CourseFolders/OperatingSystems/Projects
 /YourProjects/project3_DouglasAdolph/FILE_NAME da1140@zeus.cs.txstate.edu:

 (2) program compiled via:

 gcc p3_part1_DouglasAdolph.c -o p3p1 -lpthread -lm
 gcc p3_part2_DouglasAdolph.c -o p3p2 -lpthread -lm
 ///////////////////////////////////////////////////////////////////////////////
 -------------------------------------------------------------------------------
 *******************************************************************************
 ******************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <math.h>

#define NUM_PIRATE_THREADS 4 /* number of pirate threads to run */
#define OPEN_SESAME 0 /* single bit value representation of password */
#define OPEN_WATERMELON 1 /* single bit value representation of password */

/* percent value for "open, sesame" */
const double OPEN_SESAME_PERCENTAGE = 0.10;

/* percent value for "open, watermelon" */
const double OPEN_WATERMELON_PERCENTAGE = 0.15;

/* array of pirate threads */
static pthread_t *thread_handles;

/* mutual exlusion lock; mutual exclusion considered in this version */
pthread_mutex_t mutex;

/* pirate thread function */
void *pirate(void* rank);

/* total number of items in resource (pearls in the cave) */
static double cavePearls = 1000.00;

/* array to store the number of pearls garnered by each pirate */
int piratesBooty[NUM_PIRATE_THREADS];

/* main function */
int main() {

    /* alert user pirate threads are about to begin consuming pearls */
    printf("\nAvast matey, we are a'comin' fer yee pearls!!\n\n");

    /* index variable for pirate threads */
    long threadIndex;

    /* char variable for pirate thread labeling (i.e. 1, 2, 3, ...) */
    char alphaForPirate = 'A';

    /* create and allocate memory for thread_handles array */
    thread_handles = (pthread_t*)malloc(NUM_PIRATE_THREADS*sizeof(pthread_t));

    /* create and run pirate threads...YAR!*/
    for (threadIndex = 0; threadIndex < NUM_PIRATE_THREADS; ++threadIndex) {
        pthread_create(&thread_handles[threadIndex], NULL,
                       pirate, (void*)threadIndex);
    }

    /* join pirate threads...AVAST MATEY!*/
    for (threadIndex = 0; threadIndex < NUM_PIRATE_THREADS; ++threadIndex) {
        pthread_join(thread_handles[threadIndex], NULL);
    }

    /* update your final cave pearl number to a whole integer value */
    cavePearls = ceil(cavePearls);

    /* display pearl data after pirate thread(s) execution */
    printf("\nYar!! The cave be empty!!\n\n");
    for (threadIndex = 0; threadIndex < NUM_PIRATE_THREADS; ++threadIndex){
        printf("Pirate %c got %d pearls\n",
               alphaForPirate, piratesBooty[threadIndex]);
        alphaForPirate++;
    }
    printf("\n");

    /* free memory */
    free(thread_handles);

    return 0;
} /* end of main() */

void *pirate(void* rank) {

    /* amount of pearls pirate thread(s) may take during current entry to cave */
    static double pearlsToGrab = 0;

    /* variables to output pirate thread(s) pearl consumption */
    char alphaForPirate = 'A';
    int piratePercentage;

    /* label pirate thread(s) alphanumerically */
    alphaForPirate += (long)rank;

    while(1) { /* continue execution while pearls remain in cave */
        if (cavePearls < 1) /* cave has been emptied, pirate thread(s) should stop */
            return 0;

        /* identify which pirate thread you are currently executing */
        long my_rank = (long)rank;

        /* if pirate thread is even: "Open, sesame" pirate */
        if (my_rank % 2 == OPEN_SESAME) {

            /* CRITICAL SECTION LOCKED */
            pthread_mutex_lock(&mutex);

            piratePercentage = (OPEN_SESAME_PERCENTAGE * 100);
            pearlsToGrab = cavePearls * OPEN_SESAME_PERCENTAGE;
            pearlsToGrab = ceil(pearlsToGrab);
            cavePearls = cavePearls - pearlsToGrab;
            piratesBooty[my_rank] += pearlsToGrab;

            printf("Pirate %c gets %.0f of the pearls, %d percent of %.0f pearls available in cave\n",
                   alphaForPirate, pearlsToGrab, piratePercentage, (cavePearls + pearlsToGrab));

            /* CRITICAL SECTION UNLOCKED */
            pthread_mutex_unlock(&mutex);
        }
        /* if pirate thread is odd: "Open, watermelon" pirate */
        else if (my_rank % 2 == OPEN_WATERMELON){

            /* CRITICAL SECTION LOCKED */
            pthread_mutex_lock(&mutex);

            piratePercentage = (OPEN_WATERMELON_PERCENTAGE * 100);
            pearlsToGrab = cavePearls * OPEN_WATERMELON_PERCENTAGE;
            pearlsToGrab = ceil(pearlsToGrab);
            cavePearls = cavePearls - pearlsToGrab;
            piratesBooty[my_rank] += pearlsToGrab;

            printf("Pirate %c gets %.0f of the pearls, %d percent of %.0f pearls available in cave\n",
                   alphaForPirate, pearlsToGrab, piratePercentage, (cavePearls + pearlsToGrab));

            /* CRITICAL SECTION UNLOCKED */
            pthread_mutex_unlock(&mutex);
        }

        /* make pirate thread(s) sleep for 2 seconds */
        sleep(2);

    } /* end of while-loop */

    /* have pirate thread(s) terminate */
    pthread_exit((void*)0);
}

【问题讨论】:

  • (void *(*)(void *))pirate pirate 前面是什么乱七八糟的东西?据我所知piratepthread_create 期望的函数签名相匹配.. 只需执行pthread_create(&amp;thread_handles[currentThread], NULL, pirate, (void*)currentThread);
  • 还推荐return 0; --> break; 并将pearlsToGrab 的声明移动到pirate 函数中。
  • 想想如果pearlsToGrab 接近但不完全为零会发生什么......
  • @AndrewHenle:没什么特别的。除了在应用ceil 之前的临时步骤(并且互斥锁被保存在那里,所以没有其他东西可以看到它),它是一个整数。

标签: c multithreading pthreads posix


【解决方案1】:

在调试器中运行它直到它挂起,然后进入它并查看状态。我怀疑对于某些处决,cavePearls 变为负数,您的线程只会继续运行。 if (cavePearls == 0) 应该是 `if (cavePearls

【讨论】:

  • @DouglasAdolph 正如这个答案中所建议的,对浮点之间的精确比较要非常小心(也就是说,避免这样做):stackoverflow.com/questions/588004/…
  • 这不是问题。编写的代码使用double 只是为了存储整数(在它们完全适合的范围内)。除了ceil 之前的步骤外,任何地方都没有舍入。
  • @R..,与双精度或整数无关,请考虑 15 和 10 的组合加起来正好为 100。随机线程计时很可能会导致组合不会。跨度>
【解决方案2】:

至少,这一行是无效的,会导致你的程序有未定义的行为:

        if (cavePearls == 0) /* cave has been emptied, pirates should stop */

如果不持有锁以相对于其他线程的访问对您的访问进行排序,则您无法访问共享的可变数据。

【讨论】:

  • 几乎肯定不是问题。即使假设不能以原子方式读取/写入 double ,这些位最终也会稳定为零或非零值。但是我同意测试应该受到保护。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-01-27
  • 2015-01-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多