C #에 대한 더 나은 대기 패턴이 있습니까?
나는 이런 종류의 것을 몇 번 코딩하는 것을 발견했습니다.
for (int i = 0; i < 10; i++)
{
if (Thing.WaitingFor())
{
break;
}
Thread.Sleep(sleep_time);
}
if(!Thing.WaitingFor())
{
throw new ItDidntHappenException();
}
잘못된 코드처럼 보입니다. 더 나은 방법이 있습니까? / 잘못된 디자인의 증상입니까?
이 패턴을 구현하는 훨씬 더 좋은 방법은 Thing
객체가 소비자가 기다릴 수있는 이벤트를 노출하도록하는 것입니다. 예를 들어 ManualResetEvent
또는 AutoResetEvent
. 이것은 소비자 코드를 다음과 같이 크게 단순화합니다.
if (!Thing.ManualResetEvent.WaitOne(sleep_time)) {
throw new ItDidntHappen();
}
// It happened
Thing
측면 의 코드 도 더 이상 복잡하지 않습니다.
public sealed class Thing {
public readonly ManualResetEvent ManualResetEvent = new ManualResetEvent(false);
private void TheAction() {
...
// Done. Signal the listeners
ManualResetEvent.Set();
}
}
사용 이벤트.
완료 될 때 (또는 할당 된 시간 내에 완료되지 않은 경우) 이벤트를 발생시키고 대기중인 항목이 이벤트를 발생시킨 다음 기본 애플리케이션에서 이벤트를 처리하도록합니다.
그렇게하면 Sleep
루프 가 없습니다 .
프로그램이 기다리는 동안 (예를 들어 DB에 연결하는 동안) 수행 할 다른 작업이 없다면 루프는 무언가를 기다리는 끔찍한 방법이 아닙니다. 그러나 귀하의 문제가 몇 가지 있습니다.
//It's not apparent why you wait exactly 10 times for this thing to happen
for (int i = 0; i < 10; i++)
{
//A method, to me, indicates significant code behind the scenes.
//Could this be a property instead, or maybe a shared reference?
if (Thing.WaitingFor())
{
break;
}
//Sleeping wastes time; the operation could finish halfway through your sleep.
//Unless you need the program to pause for exactly a certain time, consider
//Thread.Yield().
//Also, adjusting the timeout requires considering how many times you'll loop.
Thread.Sleep(sleep_time);
}
if(!Thing.WaitingFor())
{
throw new ItDidntHappenException();
}
요컨대, 위의 코드는 "재시도 루프"처럼 보이며 시간 초과처럼 작동하도록 엉망이되었습니다. 타임 아웃 루프를 구성하는 방법은 다음과 같습니다.
var complete = false;
var startTime = DateTime.Now;
var timeout = new TimeSpan(0,0,30); //a thirty-second timeout.
//We'll loop as many times as we have to; how we exit this loop is dependent only
//on whether it finished within 30 seconds or not.
while(!complete && DateTime.Now < startTime.Add(timeout))
{
//A property indicating status; properties should be simpler in function than methods.
//this one could even be a field.
if(Thing.WereWaitingOnIsComplete)
{
complete = true;
break;
}
//Signals the OS to suspend this thread and run any others that require CPU time.
//the OS controls when we return, which will likely be far sooner than your Sleep().
Thread.Yield();
}
//Reduce dependence on Thing using our local.
if(!complete) throw new TimeoutException();
가능한 경우 비동기 처리를 Task<T>
. 이것은 모든 세계의 최고를 제공합니다.
- 작업 연속 을 사용하여 이벤트와 같은 방식으로 완료에 응답 할 수 있습니다 .
- 을
Task<T>
구현 하기 때문에 완료의 대기 가능 핸들을 사용하여 기다릴 수 있습니다IAsyncResult
. - 작업은 다음을 사용하여 쉽게 구성 할 수 있습니다
Async CTP
. 그들은 또한Rx
. - 작업에는 매우 깨끗한 내장 예외 처리 시스템이 있습니다 (특히 스택 추적을 올바르게 유지함).
타임 아웃을 사용해야하는 경우 Rx 또는 Async CTP가이를 제공 할 수 있습니다.
WaitHandle 클래스를 살펴 보겠습니다 . 특히 개체가 설정 될 때까지 대기 하는 ManualResetEvent 클래스입니다. 시간 제한 값을 지정하고 나중에 설정되었는지 확인할 수도 있습니다.
// Member variable
ManualResetEvent manual = new ManualResetEvent(false); // Not set
// Where you want to wait.
manual.WaitOne(); // Wait for manual.Set() to be called to continue here
if(!manual.WaitOne(0)) // Check if set
{
throw new ItDidntHappenException();
}
Thread.Sleep
항상 호출 은 피해야하는 활성 대기입니다.
한 가지 대안은 타이머를 사용하는 것입니다. 더 쉽게 사용할 수 있도록 클래스로 캡슐화 할 수 있습니다.
나는 보통 예외를 던지는 것을 권장하지 않습니다.
// Inside a method...
checks=0;
while(!Thing.WaitingFor() && ++checks<10) {
Thread.Sleep(sleep_time);
}
return checks<10; //False = We didn't find it, true = we did
AutoResetEvents를 사용해야한다고 생각합니다. 다른 스레드가 작업을 완료 할 때까지 기다릴 때 잘 작동합니다.
예:
AutoResetEvent hasItem;
AutoResetEvent doneWithItem;
int jobitem;
public void ThreadOne()
{
int i;
while(true)
{
//SomeLongJob
i++;
jobitem = i;
hasItem.Set();
doneWithItem.WaitOne();
}
}
public void ThreadTwo()
{
while(true)
{
hasItem.WaitOne();
ProcessItem(jobitem);
doneWithItem.Set();
}
}
다음과 System.Threading.Tasks
같이 할 수 있습니다 .
Task t = Task.Factory.StartNew(
() =>
{
Thread.Sleep(1000);
});
if (t.Wait(500))
{
Console.WriteLine("Success.");
}
else
{
Console.WriteLine("Timeout.");
}
But if you can't use Tasks for some reason (like a requirement of .Net 2.0) then you can use ManualResetEvent
as mentioned in JaredPar's answer or use something like this:
public class RunHelper
{
private readonly object _gate = new object();
private bool _finished;
public RunHelper(Action action)
{
ThreadPool.QueueUserWorkItem(
s =>
{
action();
lock (_gate)
{
_finished = true;
Monitor.Pulse(_gate);
}
});
}
public bool Wait(int milliseconds)
{
lock (_gate)
{
if (_finished)
{
return true;
}
return Monitor.Wait(_gate, milliseconds);
}
}
}
With the Wait/Pulse approach you don't explicitly create Events so you don't need to care about disposing them.
Usage example:
var rh = new RunHelper(
() =>
{
Thread.Sleep(1000);
});
if (rh.Wait(500))
{
Console.WriteLine("Success.");
}
else
{
Console.WriteLine("Timeout.");
}
참고URL : https://stackoverflow.com/questions/6997291/is-there-a-better-waiting-pattern-for-c
'Programing' 카테고리의 다른 글
F # : mutable 대 ref (0) | 2020.10.12 |
---|---|
Haskell : 왜 도우미 함수의 이름을“go”로 명명하는 규칙이 있습니까? (0) | 2020.10.12 |
Chart.js 선 차트의 레이블 수 제한 (0) | 2020.10.12 |
Mathematica : 기호 프로그래밍이란 무엇입니까? (0) | 2020.10.12 |
JavaScript를 사용하여 동적으로 링크에 "href"속성을 추가하려면 어떻게해야합니까? (0) | 2020.10.12 |