´ÙÀ½ ÀÌÀü Â÷·Ê

2. ±×¸®°í ¾à°£ÀÇ ½ÇÁ¦

2.1 pthread.h Çì´õ

LinuxThreads°¡ Á¦°øÇÏ´Â °ÍÀº ¾²·¹µå ·çƾµéÀÇ ÇÁ·ÎÅäŸÀÔÀ» ¼±¾ðÇÏ´Â /usr/include/pthread.h Çì´õ¸¦ ÅëÇؼ­ ÀÌ¿ë °¡´ÉÇÏ´Ù.

´ÙÁß ¾²·¹µå ÇÁ·Î±×·¥ÀÇ ÀÛ¼ºÀº ±âº»ÀûÀ¸·Î µÎ ´Ü°èÀÇ °úÁ¤ÀÌ´Ù:

¸î °¡Áö ±âº»ÀûÀÎ pthread.hÀÇ ·çƾµéÀ» °£´ÜÈ÷ ¼³¸íÇϸ鼭 ÀÌ µÎ ´Ü°è¸¦ ½ÇÆ캸ÀÚ.

2.2 lockÀÇ ÃʱâÈ­

Çؾ߸¸ Çϴ ù¹ø° Çൿµé ÁßÀÇ Çϳª´Â ¸ðµç lockµéÀ» ÃʱâÈ­ÇÏ´Â °ÍÀÌ´Ù. POSIX lockµéÀº pthread_mutex_t ŸÀÔÀÇ º¯¼ö·Î ¼±¾ðµÈ´Ù; °¢ lockÀ» ÃʱâÈ­Çϱâ À§Çã ´ÙÀ½ ·çƾÀ» È£ÃâÇÒ ÇÊ¿ä°¡ ÀÖ´Ù:

int pthread_mutex_init(pthread_mutex_t *mutex, 
const pthread_mutexattr_t *mutexattr);

¹­¾î¼­ º¸¸é:

#include <pthread.h>
...
        pthread_mutex_t lock;
        pthread_mutex_init(&lock, NULL);
...

pthread_mutex_init ÇÔ¼ö´Â mutex ÀÎÀÚ°¡ °¡¸£Å°´Â mutex °´Ã¼¸¦ mutexattr¿¡ ÀÇÇØ ¸í½ÃµÈ mutex ¼Ó¼º¿¡ µû¶ó ÃʱâÈ­¸¦ ÇÑ´Ù. mutexattrÀÇ NULLÀ̸é, µðÆúÆ® ¼Ó¼ºÀÌ »ç¿ëµÈ´Ù.

°è¼ÓÇؼ­ ÀÌ ÃʱâÈ­µÈ lockµéÀ» ¾î¶»°Ô »ç¿ëÇÏ´ÂÁö º¸°Ú´Ù.

2.3 ¾²·¹µå »ý¼ºÇϱâ

POSIX´Â °¢ ¾²·¹µå¸¦ ³ªÅ¸³»±â À§ÇØ »ç¿ëÀÚ°¡ pthread_t ŸÀÔÀÇ º¯¼ö¸¦ ¼±¾ðÇϵµ·Ï ÇÑ´Ù. ´ÙÀ½ È£Ãâ·Î ¾²·¹µå°¡ »ý¼ºµÈ´Ù:

int pthread_create(pthread_t *thread, pthread_attr_t *attr, void 
*(*start_routine)(void *), void *arg);

¼º°øÇÑ´Ù¸é »õ·ÎÀÌ »ý¼ºµÈ ¾²·¹µåÀÇ id°¡ thread ÀÎÀÚ°¡ Áö½ÃÇÑ ¿µ¿ª¿¡ ÀúÀåÀÌ µÇ°í 0ÀÎ ¸®ÅϵȴÙ. ¿¡·¯°¡ ¹ß»ýÇϸé 0ÀÌ ¾Æ´Ñ °ªÀÌ ¸®ÅϵȴÙ.

f() ·çƾÀ» ¼öÇàÇÏ´Â ¾²·¹µå¸¦ ¸¸µé°í f()¿¡ arg º¯¼ö¸¦ °¡¸£Å°´Â Æ÷ÀÎÅ͸¦ ³Ñ±â±â À§Çؼ­´Â ´ÙÀ½°ú °°ÀÌ ÇÑ´Ù:

#include <pthread.h>
...
        pthread_t thread;
        pthread_create(&thread, NULL, f, &arg).
...

f() ·çºóÀº ´ÙÀ½°ú °°Àº ÇÁ·ÎÅäŸÀÔÀ» °¡Á®¾ß ÇÑ´Ù:

void *f(void *arg);

2.4 ±ú²ýÇÑ Á¾·á

¸¶Áö¸· ´Ü°è·Î f() ·çƾÀÇ °á°ú¸¦ Á¢±ÙÇϱâ Àü¿¡ ¸¸µç ¸ðµç ¾²·¹µå°¡ Á¾·á ÇÒ ¶§±îÁö ±â´Ù·Á¾ß ÇÑ´Ù. ´ÙÀ½À» È£ÃâÇÑ´Ù:

int pthread_join(pthread_t th, void **thread_return);

th °¡ °¡¸£Å°´Â ¾²·¹µå°¡ Á¾·áÇÒ ¶§±îÁö À§ÀÇ ÇÔ¼ö¸¦ È£ÃâÇÑ ¾²·¹µåÀÇ ¼öÇàÀ» ¸ØÃá´Ù. ¸¸¾à thread_returnÀÌ NULLÀ̴ϸé thÀÇ ¸®ÅÏ°ªÀº thread_returnÀÌ °¡¸®Å°´Â ¿µ¿ª¿¡ ÀúÀåµÈ´Ù.

2.5 ¾²·¹µå ·çƾ¿¡ µ¥ÀÌÅÍ Àü´ÞÇϱâ

È£ÃâÇÑ ·çƾÀÇ Á¤º¸¸¦ ¾²·¹µå ·çƾ¿¡ ³Ñ±â´Â µÎ °¡Áö ¹æ¹ýÀÌ ÀÖ´Ù:

µÎ¹ø° °ÍÀÌ ÄÚµåÀÇ ¸ðµâ¼º(modularity)À» º¸ÀüÇÏ´Â µ¥ °¡Àå ÁÁÀº ¼±ÅÃÀÌ´Ù. ±¸Á¦Ã¼´Â ¼¼ °¡Áö ´Ü°èÀÇ Á¤º¸¸¦ Æ÷ÇÔÇØ¾ß ÇÑ´Ù; ù°·Î °øÀ¯ º¯¼öµé°ú lockµé¿¡ °üÇÑ Á¤º¸, µÎ¹ø°·Î ·çƾ¿¡¼­ ÇÊ¿ä·Î ÇÏ´Â ¸ðµç µ¥ÀÌÅÍ¿¡ ´ëÇÑ Á¤º¸, ¼¼¹ø°·Î ¾²·¹µå¸¦ ±¸ºÐÇØÁÖ´Â id¿Í ¾²·¹µå°¡ ÀÌ¿ëÇÒ ¼ö ÀÖ´Â CPUÀÇ ¼ö¿¡ ´ëÇÑ Á¤º¸ (·±Å¸ÀÓ¿¡ ÀÌ Á¤º¸¸¦ Á¦°øÇÏ´Â °ÍÀÌ ´õ ½±´Ù). ±¸Á¶Ã¼ÀÇ Ã¹¹ø° ¿ä¼ÒÀ» »ìÆ캸ÀÚ; ³Ñ°ÜÁø Á¤º¸´Â ¸ðµç ¾²·¹µåµé »çÀÌÀÇ °øÀ¯µÇ´Â °ÍÀ̾î¾ß ÇÑ´Ù. ±×·¡¼­ ÇÊ¿äÇÑ º¯¼öµé°ú lockµéÀÇ Æ÷ÀÎÅ͸¦ »ç¿ë ÇØ¾ß ÇÑ´Ù. double ŸÀÔÀÇ °øÀ¯ º¯¼ö var¿Í ±× ¿¡ ´ëÇÑ lockÀ» ³Ñ±â±â À§ÇØ ±¸Á¶Ã¼´Â µÎ ¸â¹ö º¯¼ö¸¦ °¡Á®¾ß¸¸ ÇÑ´Ù:

double volatile *var;
pthread_mutex_t *var_lock;

volatile ¼Ó¼ºÀÇ »ç¿ë À§Ä¡¿¡ ÁÖ¸ñÇ϶ó. ÀÌ´Â Æ÷ÀÎÅÍ ÀÚü°¡ ¾Æ´Ï¶ó var°¡ volatileÀÓÀ» ³ªÅ¸³½´Ù.

2.6 º´·Ä ÄÚµåÀÇ ¿¹

¾²·¹µå¸¦ ÀÌ¿ëÇÏ¿© ½±°Ô º´·ÄÈ­¸¦ ÇÒ ¼ö ÀÖ´Â ÇÁ·Î±×·¥ÀÇ ¿¹´Â µÎ º¤ÅÍÀÇ ½ºÄ®¶óÄÚ°öÀ» °è»êÀÌ´Ù. ÁÖ¼®À» ºÙÀÎ Äڵ带 Á¦½ÃÇÑ´Ù.

/* ÄÄÆÄÀÏ ÇÏ·Á¸é gcc  -D_REENTRANT -lpthread */

#include <stdio.h>
#include <pthread.h>

/* ¾Ë¸ÂÀº ±¸Á¶Ã¼ ¼±¾ð */ 
typedef struct {
        double volatile *p_s;      /* ½ºÄ®¶ó °öÀÇ °øÀ¯ º¯¼ö */
        pthread_mutex_t *p_s_lock; /* º¯¼ö sÀÇ lock */
        int n;                     /* ¾²·¹µåÀÇ ¼ö */
        int nproc;                 /* ÀÌ¿ëÇÒ ¼ö ÀÖ´Â ÇÁ·Î¼¼¼­ÀÇ ¼ö */
        double *x;                 /* ù¹ø° º¤ÅÍÀÇ µ¥ÀÌÅÍ */
        double *y;                 /* µÎ¹ø° º¤ÅÍÀÇ µ¥ÀÌÅÍ */
        int l;                     /* º¤ÅÍÀÇ ±æÀÌ */
} DATA;

void *SMP_scalprod(void *arg)
{
        register double localsum;
        long i;
        DATA D = *(DATA *)arg;

        localsum = 0.0;

        /* °¢ ¾²·¹µå´Â i = D.n¿¡¼­ ºÎÅÍ ½ºÄ®¶ó °öÀ» ½ÃÀÛÇÑ´Ù. 
           D.n = 1, 2, ...
           D.nproc °ªÀ» °®´Â´Ù. Á¤È®È÷ D.nproc°³ÀÇ ¾²·¹µå°¡ ÀÖ±â 
           ¶§¹®¿¡ iÀÇ Áõ°¡ °°Àº D.nprocÀÌ´Ù. */

        for(i = D.n; i < D.l; i += D.nproc)
        localsum += D.x[i]*D.y[i];

        /* s¿¡ ´ëÇÑ lockÀ» °Ç´Ù ... */
        pthread_mutex_lock(D.p_s_lock);

        /* ... sÀÇ °ªÀ» ¹Ù²Û´Ù. ... */
        *(D.p_s) += localsum;

        /* ... ±×¸®°í lock¸¦ Á¦°ÅÇÑ´Ù. */
        pthread_mutex_unlock(D.p_s_lock);

        return NULL;
}

#define L 9     /* º¤ÅÍÀÇ Â÷¿ø */

int main(int argc, char **argv)
{
        pthread_t *thread;
        void *retval;
        int cpu, i;
        DATA *A;
        volatile double s = 0; /* °øÀ¯ º¯¼ö */ 
        pthread_mutex_t s_lock; 
        double x[L], y[L];

        if (argc != 2) {
                printf("usage: %s <number of CPU>\n", argv[0]);
                exit(1);
        }
        
        cpu = atoi(argv[1]);
        thread = (pthread_t *) calloc(cpu, sizeof(pthread_t));
        A = (DATA *) calloc(cpu, sizeof(DATA));

 
        for (i = 0; i < L; i++)
        x[i] = y[i] = i;

        /* lock º¯¼ö¸¦ ÃʱâÈ­ÇÑ´Ù. */
        pthread_mutex_init(&s_lock, NULL);

        for (i = 0; i < cpu; i++) {
                /* ±¸Á¶Ã¼¸¦ ÃʱâÈ­ÇÑ´Ù. */
                A[i].n = i; /* ¾²·¹µåÀÇ ¼ö */
                A[i].x = x;
                A[i].y = y;
                A[i].l = L;
                A[i].nproc = cpu; /* CPUÀÇ ¼ö */
                A[i].p_s = &s;
                A[i].p_s_lock = &s_lock;

                if (pthread_create(&thread[i], NULL, SMP_scalprod, 
                    &A[i])) {
                        fprintf(stderr, "%s: cannot make thread\n", 
                                argv[0]);
                        exit(1);
                }
        }

        for (i = 0; i < cpu; i++) {
                if (pthread_join(thread[i], &retval)) {
                        fprintf(stderr, "%s: cannot join thread\n", 
                                argv[0]);
                        exit(1);
                }
        }

        printf("s = %f\n", s);
        exit(0);
}

Copyright ¨Ï 1999, Matteo Dell'Omodarme

Published in Issue 48 of Linux Gazette, December 1999


´ÙÀ½ ÀÌÀü Â÷·Ê