pthread 관련하여 특정 스레드가 수행이 안되는 문제점

lv-99의 이미지

스레드 수가 4개라고 가정하고 아래와 같은 코드를 돌리면 threads[0]을 제외하고 threads[1]부터 threads[3]까지만 수행이 되는 것 같습니다. threads[0]이 정상적으로 작동하려면 어떻게 해야하나요? 도저히 모르겠어서 질문 올립니다. 제가 무엇을 잘못하고 있나요?
pthread_create의 인자로는 구조체를 만들어 넘겨주었습니다.

예상 출력 결과로는
1 2 3 4 5
1 2 3 4 5
1 2 3 4 5
1 2 3 4 5
1 2 3 4 5
에서

1 0 0 0 0
1 2 0 0 0
1 2 3 0 0
1 2 3 4 0
1 2 3 4 5
이런 출력 결과를 원했지만 threads[0]이 정상 작동하지 않아서

1 2 3 4 5
1 2 0 0 0
1 2 3 0 0
1 2 3 4 0
1 2 3 4 5
이런 결과가 출력이 됩니다

아래는 전체 소스코드이고,

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <time.h>
#include <sys/shm.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <pthread.h>
#include <assert.h>
typedef struct arr {
	int q; //현 스레드
 
	float a;
	float b;
	float x;
}ARR;
 
int n = 5; //배열의 크기
int tnum = 4; //총 스레드 수
int i = 0;
pthread_barrier_t barrier;
 
int start2(int n, int tnum, int q, int i) {
	int m = i;
	int r = m % tnum;
	int blockStart;
	if (q < r) {
		blockStart = (m / tnum + 1) * q;
	}
	else if (q >= r) {
		blockStart = (m / tnum + 1) * r + (m / tnum) * (q - r);
	}
}
int end2(int n, int tnum, int q, int i) {
	int m = i;
	int r = m % tnum;
	int blockEnd;
	if (q < r) {
		blockEnd = (m / tnum + 1) * (q + 1) - 1;
	}
	else if (q >= r) {
		blockEnd = (m / tnum + 1) * r + (m / tnum) * (q - r + 1) - 1;
	}
	return blockEnd;
}
 
void* parBackSubstitution(void* ary);
 
int main() {
	pthread_t threads[tnum];
	int rc;
	pthread_barrier_init(&barrier, NULL, tnum + 1);
 
	//구조체에 값 부여
	ARR* ary = (ARR*)malloc(sizeof(ARR) * (5 * 5));
	for (i = 0; i < 5; i++) {
		for (int j = 0; j < 5; j++) {
			ary[(i * 5) + j].a = j+1;
		}
	}
	for (i = 0; i < 5; i++) {
		ary[i].b = 1;
	}
 
	//계산
	for (i = n - 1; i >= 0; i--) {
		ary[i].x = ary[i].b / ary[i * n + i].a;
		for (int q = 0; q < tnum; q++) {
			ary[0].q = q; //구조체에 현 스레드 넘버 넣음
			rc = pthread_create(&threads[q], NULL, parBackSubstitution, ary);
			assert(rc == 0);
		}
		pthread_barrier_wait(&barrier);
	}
 
	for (int q = 0; q < tnum; q++) {
		pthread_join(threads[q], NULL);
		assert(rc == 0);
	}
 
	//출력 부분
	for (i = 0; i < n; i++) {
		for (int j = 0; j < n; j++) {
			printf("%f ", ary[i * n + j].a);
		}
		printf("\n");
	}
	printf("\n");
	for (int j = 0; j < n; j++) {
		printf("%f ", ary[j].b);
	}
	printf("\n");
 
 
	free(ary);
	pthread_exit(NULL);
	pthread_barrier_destroy(&barrier);
}
 
void* parBackSubstitution(void* ary) {
	ARR* ar = (ARR*)ary;
	int q = ar[0].q;
 
	for (int j = end2(n, tnum, q, i); j >= start2(n, tnum, q, i); j--) { //병렬화가 되어야 하는 부분
		ar[j * n + i].a = 0;
	}
 
	ary = ar;
	pthread_barrier_wait(&barrier);
}

아래가 pthread_create 내용이 담긴 코드입니다.

	for (i = n - 1; i >= 0; i--) {
		ary[i].x = ary[i].b / ary[i * n + i].a;
		for (int q = 0; q < tnum; q++) {
			ary[0].q = q;
			rc = pthread_create(&threads[q], NULL, parBackSubstitution, ary);
			assert(rc == 0);
		}
		pthread_barrier_wait(&barrier);
	}
Anti-Lock의 이미지

스레드가 실행이 안되지는 않을테고...

올리신 코드는 변수 i가 전역변수인데..
지역변수여야 할거 같습니다?

lv-99의 이미지

의견 감사합니다.

parBackSubstitution라는 함수에서 i를 꼭 써야했기에 구조체에 int i를 추가해보았습니다.
메인 함수에서는 계산하는 부분이 아주 약간 바뀌어 이런 코드가 되었고,

for (int i = n - 1; i >= 0; i--) {
		ary[0].i = i;
		ary[i].x = ary[i].b / ary[i * n + i].a;
		for (int q = 0; q < tnum; q++) {
			ary[0].q = q; //구조체에 현 스레드 넘버 넣음
			rc = pthread_create(&threads[q], NULL, parBackSubstitution, ary);
			assert(rc == 0);
		}
		pthread_barrier_wait(&barrier);
	}

함수부분도 그에 맞게 아래와 같이 바꿔보았습니다.

void* parBackSubstitution(void* ary) {
	ARR* ar = (ARR*)ary;
	int q = ar[0].q;
	int i = ar[0].i;
 
	for (int j = end2(n, tnum, q, i); j >= start2(n, tnum, q, i); j--) { //병렬화가 되어야 하는 부분
		ar[j * n + i].a = 0;
	}
 
	ary = ar;
	pthread_barrier_wait(&barrier);
}

그러나 여전히 threads[0]이 함수를 제대로 수행하지 않습니다...
혹시 pthread_barrier_wait의 위치가 잘못되었을까요?
익명 사용자의 이미지

1. 변수 i를 전역 변수로 만드는 건 상상도 못할 짓입니다;

설령 기능적으로는 문제가 없다고 해도, 사람들은 i와 같은 변수는 당연히 지역적으로 사용할 거라고 예상하기 때문이죠.
그런 기대를 저버리는 건 절대 좋다고 할 수가 없습니다.

뭐, 지금은 고치셨으니 그건 넘어가고...

2. 모든 스레드가 동일한 ARY 주소를 공유하는군요.

ary를 동적 배열로 잡으신 걸 보면 스레드마다 하나씩 주고 싶었던 것 같은데, 왜 모든 스레드에 똑같이 주십니까?

보내는 쪽에서는 항상 이렇게 주고

ary[0].q = q; //구조체에 현 스레드 넘버 넣음
rc = pthread_create(&threads[q], NULL, parBackSubstitution, ary);

받는 쪽에서는 항상 이렇게 받기 때문에

ARR* ar = (ARR*)ary;
int q = ar[0].q;

결국 모든 스레드가 ary[0]만 바라보고 있는 셈이 됩니다.

data race가 발생하는 건 더 말할 것도 없고요. 첫 스레드가 q값을 읽기 전에 메인 스레드에서 q를 바꿔 버리면 어쩌죠?

더 말할 것도 없이 ARY 구조체 셋업은 스레드마다 따로 해 주고, pthread_create에서 ARY 주소를 넘길 때도 각각 다르게 주어야 합니다.

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.