다중 스레딩 애플리케이션을 우아하게 닫습니까?
-
20-08-2019 - |
문제
두 개의 스레드가있는 응용 프로그램이 있습니다.
소켓을 사용하여 데이터를 캡처하고 DataTables를 업데이트하는 첫 번째 (기본 스레드)
두 번째는 데이터 레이스를 데이터베이스에 삽입합니다.
응용 프로그램은 잘 작동하지만 닫히면 메인 스레드가 데이터를 읽고 두 번째 스레드에서 호출 중 Abort 메소드를 완료하여 데이터베이스에 삽입 될 수 있으며 이는 일관되지 않은 데이터로 이어집니다.
현재 "삽입 중 낙태"를 극복하기 위해 다음 솔루션을 사용하고 있습니다.
편집 : 강력한 답변 후 코드를 변경했습니다.
void MainThread()
{
while(Read())
{
//Read Data through socket
try
{
//Wait on Mutex1
//Update Tables
}
finally
{
//Release Mutex1
}
}
_isrunning = false;
_secondThread.Join();
}
void SecondThread()
{
while(_isrunning)
{
try
{
//Wait on Mutex1
//Insert Tables into Database using transactions
}
finally
{
//Release Mutex1
}
}
}
해결책
"Call Abort 메소드"를 가정하면 Thread.Abort를 사용하여 스레드를 중단하는 것을 의미합니다. 그렇게하지 마십시오.
당신은 앱을 효과적으로 충돌시키고 있습니다. 모니터 로이 작업을 수행하는 더 깨끗한 방법이 있습니다.
그럼에도 불구하고 앱이 충돌 할 때 DB에서 일관성이없는 데이터를 얻지 않아야합니다. 산 속성.
매우 중요한 편집당신은 다음과 같이 말했습니다 : 당신은 성능상의 이유로 트랜잭션을 사용하지 않고 대신 뮤텍스를 사용합니다. 이것은 잘못된 꽤 많은 수준에서. 첫째, 트랜잭션은 특정 작업을 더 빨리 만들 수 있습니다. 예를 들어 10 행을 테이블에 삽입하려고 시도하고 트랜잭션 내에서 다시 시도하면 트랜잭션 버전이 더 빠릅니다. 둘째, 앱이 충돌하면 어떻게됩니까? DB가 손상 되었습니까? 앱의 여러 인스턴스가 실행되면 어떻게됩니까? 아니면 쿼리 분석기에서 DB에 대해 보고서를 실행하는 동안?
다른 팁
두 스레드가 배경 스레드로 표시되지 않는 한, 앱은 두 스레드가 종료 될 때까지 계속 실행됩니다. 따라서 실제로, 당신이해야 할 일은 각 스레드를 개별적으로 깨끗하게 빠져 나가는 것입니다. 데이터베이스에 쓰는 스레드의 경우, 이는 생산자/소비자 대기열을 소진하고 깃발을 종료하는 것을 의미 할 수 있습니다.
적합한 생산자/소비자 대기열을 보여주었습니다 여기 - 근로자는 다음과 같습니다.
void WriterLoop() {
SomeWorkItem item; // could be a `DataTable` or similar
while(queue.TryDequeue(out item)) {
// process item
}
// queue is empty and has been closed; all done, so exit...
}
다음은 전체 예입니다 SizeQueue<>
- 독자가 될 때까지 프로세스가 종료되지 않습니다. 그리고 작가는 깨끗하게 빠져 나갔다. 대기열을 배수하지 않아도되면 (예 : 더 빨리 종료하고 보류중인 작업을 잊어 버리려면), 여분의 (휘발성) 깃발을 어딘가에 추가하십시오.
static class Program {
static void Write(object message) {
Console.WriteLine(Thread.CurrentThread.Name + ": " + message);
}
static void Main() {
Thread.CurrentThread.Name = "Reader";
Thread writer = new Thread(WriterLoop);
writer.Name = "Writer";
var queue = new SizeQueue<int>(100);
writer.Start(queue);
// reader loop - note this can run parallel
// to the writer
for (int i = 0; i < 100; i++) {
if (i % 10 == 9) Write(i);
queue.Enqueue(i);
Thread.Sleep(5); // pretend it takes time
}
queue.Close();
Write("exiting");
}
static void WriterLoop(object state) {
var queue = (SizeQueue<int>)state;
int i;
while (queue.TryDequeue(out i)) {
if(i%10==9) Write(i);
Thread.Sleep(10); // pretend it takes time
}
Write("exiting");
}
}
뮤 테스 대기에는 타임 아웃이 필요합니다. 각 스레드의 외부 루프는 '지금 닫으십시오'플래그를 확인할 수 있습니다. 종료하려면 각 스레드의 '지금 닫으십시오'플래그를 설정 한 다음 '조인'을 사용하여 각 스레드가 완료 될 때까지 기다립니다.