thread 구현시 하나의 버퍼를 자주 검사하지 않을 방법이 없을까

shpark05의 이미지

안녕하세요 ?

무엇좀 그려보다가 , 이렇게 할때 효율성을 갖을 수 있을지 몰라서
글을 올려 봅니다.

아직 코드를 작성하지 않아서 , 이해 부탁드립니다.

Quote:

mutex_a // 검사
buff // 데이타

thread A 는 계속 돈다

while(1)
{
lock(mutex_a)

buff[0] == 0x00 이 아니면, 원하는 기능 수행하기
수행이 끝나면,
buff[0]= 0x00 넣기

unlock(mutex_a)

}

thread B 는 계속 데이타를 넣는다.
{

lock(mutex_a)

buff[0] == 0x00 이면, 데이타를 넣는다.

unlock(mutex_a)

}

이렇게 실행된다고 할때, B 는 여러개 띄울수 있습니다.
한 100개라고 가정한다면....

우선, A 가 너무 자주 lock 을 합니다.
B 도 여러개 있을경우 값을 넣을 수 있는 상태인지 보기 때문에
너무 자주 lock 을 한다고 볼 수 있습니다.

이럴때 , 좋은 해결 방법이 있을까요 ?

부탁드립니다.

rasungboy의 이미지

B가 여러개면 각각의 B용 버퍼를 두는게 어떨까요?

즉 하나의 버퍼를 가지고 A와 다수의 B가 락을 거니

효율이 떨어질수밖에 없죠.

즉 A에

B-1 용 버퍼에 락걸기
....
B-1 용 버퍼에 락풀기

B-2 용 버퍼에 락걸기
....
B-2 용 버퍼에 락풀기

B-3 용 버퍼에 락걸기
....
B-3 용 버퍼에 락풀기

이렇게 할경우 b-1 용 버퍼에 락이 걸릴때 b-2 나 b-3 는
락이 안걸려있으므로 좀더 효율적으로 접근할수 있겠죠..

또 a가 별다른일 없이 계속 락걸고 버퍼에 삽입하는 일만 하니
락거는 일이 아주 빈번할텐데 이럴경우 다른스레드에서 해당
버퍼에 접근할 기회가 많이 안생기니 좀 다른구조로 바꾸거나
아니면 0.1ms 마다 락을걸고 데이타를 삽입한다던지 하는식
으로 좀 바꺼서 다른스레드에서 접근할 기회를 많이 주는게
어떨까 하네요.

kihlle의 이미지

lock의 횟수가 문제가 아니라 보호구역이 넓은 것이 문제가 아닌지요.
(buff,mutex_a)가 버퍼와 이에 대한 접근을 제어하는 뮤텍스라고 할 경우, 님의 코드는
전적으로 buff를 제어하기 위해 mutex_a에 의존하므로 두 thread중 1개만이 작업가능하게
됩니다. 그렇게 구현할경우 멀티쓰레딩의 잇점은 사라집니다.
차라리 아래와 같은 경우가 더나을것입니다. (threadA,B가 각 1개씩만 존재한다면 차라리
단일프로세싱을 추천합니다)

thread A 
while(1) 
{ 
	원하는 기능을 계속 수행하기 
} 

물론 2개의 thread만으로 "Producer-Consumer model"을 구현할 경우 shpark님과 같이 구현할
가능성도 있습니다. 저는 여기서 thread A,B가 각각 100개정도 수행한다고 가정하겠습니다.
일단, 버퍼를 n벌 준비하는 것이 기본전제이구요.
mutex_a //검사
cond_a //조건변수
buff[n]	//데이타

thread A 는 계속 돈다 : 단 thread B로부터의 시그널을 1회 받으면 1회 수행.
while(1) 
{ 
int X

//--- cs start -----------
lock(mutex_a) 
cond_wait(cond_a)
unlock(mutex_a) 
//--- cs end--------------

loop( X from 0 to n) // buff[X]!=0x00이 되는 X를 찾는다.
buff[X]로 원하는 기능 수행하기. 수행이 끝나면, 
buff[X]= 0x00
} 

thread B 는 계속 데이타를 넣는다.
{ 
int X

넣을 데이타를 만든다.
//--- cs start ------------
lock(mutex_a) 
loop(X from 0 to n)  // buff[X]==0x00이 되는 X를 찾는다.
buff[X]에 데이타를 넣는다.
cond_signal(cond_a)
unlock(mutex_a) 
//---- cs end -------------
} 

플랫폼에 따라서 쓰레드별로 postmessaging도 가능할 것이고, pthread_key_* 함수로
thread-specific buffer를 지정할수도 있을 것이며, n개 버퍼의 가용여부를 체크하기위해
뮤텍스대신 세마포어를 사용하는 것이 더 나을것입니다. 님의 슈도코드는 thread 개론서
같은데나 나오는 전형적인 공급자소비자모형과 거의 같습니다만, 실제로 까다로운 것은
그에 따른 세부처리를 얼마나 효과적으로 할수있느냐 하는것이거든요.

사족을 붙이면, 꼭 필요한 최소구역이 아닌 함수전체를 보호구역으로 만드는 것은 별로
좋지 않습니다. :-)

homeless

ssehoony의 이미지

크리티컬 섹션의 크기가 문제가 아니고
쓰레드 A가 계속 루프를 도는게 비효율적이라고 느끼신것 같네요.

이건 kihlle 님께서 하신것 처럼 cond_wait 과 cond_signal, cond_signal_all 등등의 컨디션 함수를 이용해서 이벤트를 던저 주면 되는데요.
사실 kihlle 님이 예로 하신 코드는 잘 못된 코드입니다.

while(1) 
{ 
int X 

//--- cs start ----------- 
lock(mutex_a) 
cond_wait(cond_a) 
unlock(mutex_a) 
//--- cs end-------------- 

loop( X from 0 to n) // buff[X]!=0x00이 되는 X를 찾는다. 
buff[X]로 원하는 기능 수행하기. 수행이 끝나면, 
buff[X]= 0x00 
} 

위의 코드에서 buff 를 동시에 접근하는 오류를 제거 하기위해 뮤텍스를 사용한건데 저렇게 buff 를 사용하는 코드를 뮤텍스 락 밖으로 빼버리면 다른 쓰레드에서 buff 를 접근할때 위의 쓰레드도 동시에 접근할 수 있는 문제가 있져.
unlock 는 shpark05 님께서 처음 하신게 맞고 cond_XXXX 관련 함수를 공부해 보시면 해결법이 이미 마련되어 있다는것을 아실 수 있을 겁니다.
zelon의 이미지

리눅스용이라면 인터럽트 같은 게 존재할 지 모르겠지만, 일단 위도우라면 event 를 이용하는게 가능합니다.

즉, 공용으로 쓰일 event 를 정의한 후,

threadA 는 항상 그 event 만 감시합니다. 그래서 event 가 설정되어 있으면 버퍼를 검사합니다.

threadB 는 버퍼에 무언가를 넣은 후는 event 를 설정합니다.

간단한 수도 코드를 작성해보자면


bool g_event = false;  // 윈도우의 경우에는 event 사용. 리눅스는 interrupt??

threadA()
{
   while ( 1 )
   {
       if ( g_event )
      {
          // 락을 건 후 버퍼 처리
      }
   }
}

threadB()
{
    // 락을 건 후 버퍼에 넣기
    g_event = true;
}

이런식으로 하는 건 어떨까요? lock 을 거는 횟수가 많이 줄어들지 싶군요.

참고로 주제를 시작하신 분의 방식을 폴링 방식(polling) 이라고 한다더군요. 계속 열어보는 방식, 제가 적은 방식을 event 방식이라고 하지 싶습니다. ^^;;;

다른 분들의 의견도 기대해봅니다.

-----------------------------------------------------------------------
GPL 오픈소스 윈도우용 이미지 뷰어 ZViewer - http://zviewer.wimy.com
블로그 : http://blog.wimy.com

shpark05의 이미지

우선 rasungboy님,kihlle님,devilhero님,zelon님 모두 감사드립니다.

제가 궁금한것이 있습니다. 다음과 같이 될 때 입니다.

Quote:

thread A 가 폴링으로 대기한다고 가정후 , cond 도 포함

thread B1

thread B2

thread B3

... thread Bn

만약 B1에서 데이타를 쓰기위하여 lock 을 시도한후 성공하여 A 에게 브로드캐스팅 또는
폴링처리를 한다고 하였을때,

이미 B2 또는 B3 에서도 데이타를 가지고 있어서 lock 을 시도한다면,
정작 B2,B3....Bn 까지 순서대로(데이타를 모두 넣으려고 한다는 가정하에..)
lock,unlock 이후 A 가 lock 에 성공하지 않을까 생각이 듭니다.

처음의 의도는 thread A 특수한 기능을 집중화 하는게 목표였고 , Thread B.. 들에게 담긴
데이타를 빨리 thread A 에 전달하는게 다음 순서였습니다.

간단히 그려놓고 보니, thread A 가 계속 루프도는것이 devilhero님 말씀처럼 맘에 걸리고요.
zelon님 말씀처럼 폴링을 쓰려니, 성공하지 못할 작업을 위하여 B2...Bn 까지 lock,unlock을
해야하는것이 맘에 걸립니다.

혹시, 제가 강을 건너려고 하면서 배가 아닌 자동차를 생각하는게 아닌지....
방향이 잘못 되었을 수도 있다는 생각이 조금 들기도 하고, 무엇인가 답이
있을것 같기도 해서, 쉽게 포기가 않되네요.. ^^;

kihlle의 이미지

devilhero님께서는 너무 단순하게 생각하셨습니다. 100개의 버퍼중 1번버퍼와 2번버퍼를 동시에 제어할수 없고 오로지 하나의 쓰레드만이 100개의 전체버퍼더미에 접근할수 있는 시나리오를 생각하는것 같은데 그것은 우리가 원하는 결과가 아닙니다. 아예 그럴거라면 그냥 producer-consumer model의 기본에 대해 조건변수에 관한 내용만 언급하고, 버퍼를 여러벌 둔다는 얘기를 할 필요조차 없었을것입니다 . :-(

zelon님께서 인터럽트와 폴링의 차이에 대해서 말씀해주셨는데요.
제시하신 슈도코드를 그대로 옮기면 실은 계속 while()을 돌면서 체크하게 되는 폴링의 일종이라 할수있지 않나요?
직접 돌려보시면 엄청난 cpu점유율을 보실수 있을것 같습니다. :-)
만일 g_event가 거짓일경우는 ThreadA는 아래와 별반 다를바 없습니다.

while(1) {
}
pthread_mutex_lock의 횟수를 조금 줄이는 댓가치고는 좀 크지 않은가요?

사족:
발제자님의 글이 다시 올라왔는데 그다지 드릴 말씀이 없는듯 합니다. 상황을 제나름대로 생각컨대, 일단 해보시고 문제에 부딪혀보시는것이 어떨까요

homeless

zelon의 이미지

kihlle wrote:

zelon님께서 인터럽트와 폴링의 차이에 대해서 말씀해주셨는데요.
제시하신 슈도코드를 그대로 옮기면 실은 계속 while()을 돌면서 체크하게 되는 폴링의 일종이라 할수있지 않나요?
직접 돌려보시면 엄청난 cpu점유율을 보실수 있을것 같습니다. :-)
만일 g_event가 거짓일경우는 ThreadA는 아래와 별반 다를바 없습니다.
while(1) {
}
pthread_mutex_lock의 횟수를 조금 줄이는 댓가치고는 좀 크지 않은가요?

ㅎㅎ 써놓고 보니 그렇네요 ^^ 원래는 while ( 1 ) 안에서 WaitForSingleObject() 로 기다려야 하는데... 어줍잖게 리눅스를 고려하다보니 그렇군요. 원래는 이벤트(혹은 인터럽트)를 기다리는 코드가 있어야 겠죠.

Quote:

이미 B2 또는 B3 에서도 데이타를 가지고 있어서 lock 을 시도한다면,
정작 B2,B3....Bn 까지 순서대로(데이타를 모두 넣으려고 한다는 가정하에..)
lock,unlock 이후 A 가 lock 에 성공하지 않을까 생각이 듭니다.

위와 같은 경우에는 B2 ... Bn 까지 순서대로 되지 않습니다. 물론 B2 를 만들고..... 해서 Bn 까지 순서대로 생성(!) 한다면 모르지만요. 동시에 돌 경우에는 누가 먼저 처리할지 OS 만이 알겠죠.

-----------------------------------------------------------------------
GPL 오픈소스 윈도우용 이미지 뷰어 ZViewer - http://zviewer.wimy.com
블로그 : http://blog.wimy.com

댓글 달기

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
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.