클래스간의 변수공유에 대하여..

cdcmp의 이미지

안녕하세요..

현재 qt에서 시리얼 프로그램을 작성하고 있습니다.

쓰레드클래스에서 내부에서 시리얼 통신으로 데이터 입력을 받으면 다른 폼클

래스에서는 시리얼로 입력 받은 데이터를 처리하는 구조 입니다.

그런데 쓰레드에서 입력 받은 데이터를 다른 폼클래스에서 확인을 해보니까

0 이라는 값들이 많이 들어가 있더라고요...

그래서 간단하게 나마 확인을 하기 위해서 다음과 같이 프로그램을 했습니다.

threadform.cpp에서 tx_buf를 unsigned char형으로 정의를 했고, 0x02라

는 데이터를 임의로 넣었습니다.

그리고 thread.cpp에서는 위의 변수를 extern unsigned char tx_buf로

선언을 하였고, 무한 루프를 돌렸습니다.

프린트해서 확인 해본결과 2라는 데이터가 계속나와있어야 되는데 0이라는

데이터가 한참 나온 후에 2라는 데이터가 나왔습니다.

변수 설정해주는데 문제가 있는지....

클래스간에 변수공유 방법이 잘못되었는지요??

프로그램 소스는 다음과 같습니다.

<thread.cpp>

#include "thread.h"

using namespace std;

extern unsigned char tx_buf[8];

FILE *fo;

int i,k;

Thread::Thread()
{
    stopped = false;
}

void Thread::setMessage(const QString &message)
{
    messageStr = message;
}

void Thread::run()
{
    i=0;
    k=1;
  
    
    while (!stopped)
    {
	cerr << messageStr.ascii();   
 
        fo = fopen("/home/eunsu/paper/threads1/data.txt","a+");	
        fprintf(fo,"%d \n",tx_buf[0]);
        fclose(fo);
    }
    
    stopped = false;
    cerr << endl;
}

void Thread::stop()
{
    stopped = true;
}



<thread.h>

#ifndef THREAD_H
#define THREAD_H

#include <qthread.h>

class Thread : public QThread
{
public:
    Thread();

    void setMessage(const QString &message);
    void run();
    void stop();
        
protected:  
    
    
private:
    QString messageStr;
    volatile bool stopped;
};

#endif   //THREAD_H


<threadform.cpp>

#include <qlayout.h>
#include <qpushbutton.h>

#include "threadform.h"

unsigned char tx_buf[8];

int j;

FILE *tg;

ThreadForm::ThreadForm(QWidget *parent, const char *name)
    : QDialog(parent, name)
{
    setCaption(tr("Threads"));

    threadA.setMessage("A");
    threadB.setMessage("B");

    threadAButton = new QPushButton(tr("Start A"), this);
    threadBButton = new QPushButton(tr("Start B"), this);
    quitButton = new QPushButton(tr("Quit"), this);
    quitButton->setDefault(true);

    connect(threadAButton, SIGNAL(clicked()),this, SLOT(startOrStopThreadA()));
    connect(threadBButton, SIGNAL(clicked()),this, SLOT(startOrStopThreadB()));
    connect(quitButton, SIGNAL(clicked()),this, SLOT(close()));

    QHBoxLayout *mainLayout = new QHBoxLayout(this);
    mainLayout->setResizeMode(QLayout::Fixed);
    mainLayout->setMargin(200);
    mainLayout->setSpacing(8);
    mainLayout->addWidget(threadAButton);
    mainLayout->addWidget(threadBButton);
    mainLayout->addWidget(quitButton);
}

void ThreadForm::startOrStopThreadA()
{
    if (threadA.running()) 
    {
	threadA.stop();
	threadAButton->setText(tr("Start A"));
    } 

    else
    {
	threadA.start();
	threadAButton->setText(tr("Stop A"));
	
	tx_buf[0] = 0x02;
	
	j = 0;
	Timer = startTimer(1);
    }
}

void ThreadForm::startOrStopThreadB()
{
    if (threadB.running()) 
    {
        threadB.stop();
        threadBButton->setText(tr("Start B"));
    }
    else
    {
        threadB.start();
        threadBButton->setText(tr("Stop B"));
    }
}

void ThreadForm::timerEvent(QTimerEvent *event)
{
    if(event->timerId() == Timer)
    {
    }
}

void ThreadForm::closeEvent(QCloseEvent *event)
{
    threadA.stop();
    threadB.stop();
    threadA.wait();
    threadB.wait();
    event->accept();
}





<threadform.h>

#ifndef THREADFORM_H
#define THREADFORM_H

#include <qdialog.h>

#include "thread.h"

class QPushButton;

class ThreadForm : public QDialog
{
    Q_OBJECT
public:
    ThreadForm(QWidget *parent = 0, const char *name = 0);
   
protected:
    void closeEvent(QCloseEvent *event);
    void timerEvent(QTimerEvent *event);
    

private slots:
    void startOrStopThreadA();
    void startOrStopThreadB();

private:
    Thread threadA;
    Thread threadB;
    QPushButton *threadAButton;
    QPushButton *threadBButton;
    QPushButton *quitButton;
     int Timer; 
};

#endif    //THREADFORM_H


kihongss의 이미지

쓰레드간의 공유 변수를 volatile 키워드로 지정해보시기 바랍니다.

pool007의 이미지

멀티 프로세서의 멀티 쓰레드 환경에서는 portable한 코드를 쓰고 싶다면 volatile을 사용해서는 안됩니다. 그리고 특히, C/C++에서 object에 대한 volatile 변수는 "undefined"되어 있어 동작을 보장할 수 없구요. (위의 예에서 객체에 대한 변수를 선언한건지 아닌지 잘 모르겠지만요.)

작년에 이쪽 이슈에 대해서 자바 커뮤니티내에서 논쟁이 있었습니다. 그때는 double checked locking 이 왜 불가능한가에대한 문제였는데, 설명하기도 일일히 대처하기도 너무 힘들었습니다. 그래서 여기서 C/C++의 volatile에 대해서 다시 설명하기는 힘들것 같고, Compaq Posix Thread Architect분의 인용문을 적겠습니다. "Compiler volatile semantics are not sufficient when sharing flag_ between threads, because the hardware, as well as the compiler, may reorder memory accesses arbitrarily, even with volatile. (Nor would a compiler implementation that issued memory barriers at each sequence point for volatile variables be sufficient, unless ALL data was volatile, which is impractical and unreasonably expansive.)" (원문 주소: http://tinyurl.com/5nk5s)

volatile은 일반적으로 portable한 코드를 위한 동기화 construct로서는
사용이 불가능합니다. 특히나 CPU가 2개 이상일 경우에는 예기치 못한 동작을 할 수 있고요...

인용한 글은 아래 주소에서 볼 수 있습니다.

http://tinyurl.com/49lhu

첫번째 인용문은 IBM 의 아키텍트이고 두번째 인용문은 HP의 Tru64 UNIX & VMS Thread Architect 의 글입니다.

"....
> - when the 'volatile' keyword must be used in
> multithreaded programming?

Never in PORTABLE threaded programs. The semantics of the C and
C++ "volatile" keyword are too loose, and insufficient, to have
any particular value with threads. You don't need it if you're
using portable synchronization (like a POSIX mutex or semaphore)
because the semantics of the synchronization object provide the
consistency you need between threads.

The only use for "volatile" is in certain non-portable
"optimizations" to synchronize at (possibly) lower cost in
certain specialized circumstances. That depends on knowing and
understanding the specific semantics of "volatile" under your
particular compiler, and what other machine-specific steps you
might need to take. (For example, using "memory barrier"
builtins or assembly code.)

In general, you're best sticking with POSIX synchronization, in
which case you've got no use at all for "volatile". That is,
unless you have some existing use for the feature having nothing
to do with threads, such as to allow access to a variable after
longjmp(), or in an asynchronous signal handler, or when
accessing hardware device registers.

---

The C language defines a series of "sequence points" in the "abstract
language model" at which variable values must be consistent with language
rules. An optimizer is allowed substantial leeway in reordering or
eliminating sequence points to minimize loads and stores or other
computation. EXCEPT that operations involving a "volatile" variable must
conform to the sequence points defined in the abstract model: there is no
leeway for optimization or other modifications. Thus, all changes
previously made must be visible at each sequence point, and no subsequent
modifications may be visible at that point. (In other words, as C99 points
out explicitly, if a compiler exactly implements the language abstract
semantics at all sequence points then "volatile" is redundant.)

On a multiprocessor (which C does not recognize), "sequence points" can only
be reasonably interpreted to refer to the view of memory from that
particular processor. (Otherwise the abstract model becomes too expensive
to be useful.) Therefore, volatile may say nothing at all about the
interaction between two threads running in parallel on a multiprocessor.

On a high-performance modern SMP system, memory transactions are effectively
pipelined. A memory barrier does not "flush to memory", but rather inserts
barriers against reordering of operations in the memory pipeline. For this
to have any meaning across processors there must be a critical sequence on
EACH end of a transaction that's protected by appropriate memory barriers.
This protocol has no possible meaning for an isolated volatile variable,
and therefore cannot be applied.

The protocol can only be employed to protect the relationship between two
items; e.g., "if I assert this flag then this data has been written" paired
with "if I can see the flag is asserted, then I know the data is valid".

That's how a mutex works. The mutex is a "flag" with builtin barriers
designed to enforce the visibility (and exclusion) contract with data
manipulations that occur while holding the mutex. Making the data volatile
contributes nothing to this protocol, but inhibits possibly valuable
compiler optimizations within the code that holds the mutex, reducing
program efficiency to no (positive) end.

If you have a way to generate inline barriers (or on a machine that doesn't
require barriers), and you wish to build your own low-level protocol that
doesn't rely on synchronization (e.g., a mutex), then your compiler might
require that you use volatile -- but this is unspecified by either ANSI C
or POSIX. (That is, ANSI C doesn't recognize parallelism and therefore
doesn't apply, while POSIX applies no specific additional semantics to
"volatile".) So IF you need volatile, your code is inherently nonportable.

A corollary is that if you wish to write portable code, you have no need for
volatile. (Or at least, if you think you do have a need, it won't help you
any.)

In your case, trying to share (for unsynchronized read) a "volatile"
counter... OK. Fine. The use of volatile, portably, doesn't help; but as
long as you're not doing anything but "ticking" the counter, (not a lot of
room for optimization) it probably won't hurt. IF your variable is of a
size and alignment that the hardware can modify atomically, and IF the
compiler chooses the right instructions (this may be more likely with
volatile, statistically, but again is by no means required by any
standard), then the worst that can happen is that you'll read a stale
value. (Potentially an extremely stale value, unless there's some
synchronization that ensures memory visibility between the threads at some
regular interval.) If the above conditions are NOT true, then you may read
"corrupted" values through word tearing and related effects.

If that's acceptable, you're probably OK... but volatile isn't helping you.

--

따라서 최소한 원글을 쓰신분의 thread stop 은 완벽히 잘못하고 계십니다. Non-portable하며 multiprocessor 환경에서 정상 동작여부를 보장하지 못합니다.

--
Passion is like genius; a miracle.

pool007의 이미지

http://www.aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf

이 글이 충분한 설명이 되리라 생각합니다.
Scott Meyers 와 Andrei Alexandrescu가 쓴 글인데, 두 사람다 C/C++에서는 충분히 알려져있는 사람들이라 읽을 가치가 있을 것입니다. 특히 최근 자바의 concurrency에서는 매우 유명한 Doug Lea 등이 review 한 글입니다.

--
Passion is like genius; a miracle.

kihongss의 이미지

그렇군요. :D
링크하신 자료는 밤새 읽어봐야겠습니다.

댓글 달기

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