Ich habe einige Male solche Dinge programmiert.
for (int i = 0; i < 10; i++)
{
if (Thing.WaitingFor())
{
break;
}
Thread.Sleep(sleep_time);
}
if(!Thing.WaitingFor())
{
throw new ItDidntHappenException();
}
Es sieht einfach nach schlechtem Code aus. Gibt es einen besseren Weg, dies zu tun? Ist es ein Symptom für schlechtes Design?
c#
multithreading
design-patterns
wahrscheinlich am Strand
quelle
quelle
Verwenden Sie Ereignisse.
Lassen Sie das Ereignis, auf das Sie warten, ein Ereignis auslösen, wenn es beendet ist (oder nicht innerhalb der vorgegebenen Zeit beendet wurde), und behandeln Sie das Ereignis dann in Ihrer Hauptanwendung.
Auf diese Weise haben Sie keine
Sleep
Schleifen.quelle
Eine Schleife ist keine SCHRECKLICHE Möglichkeit, auf etwas zu warten, wenn Ihr Programm während des Wartens nichts anderes tun kann (z. B. während der Verbindung zu einer Datenbank). Ich sehe jedoch einige Probleme mit Ihren.
//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(); }
Kurz gesagt, der obige Code sieht eher wie eine "Wiederholungsschleife" aus, die bastardisiert wurde, um eher wie eine Zeitüberschreitung zu funktionieren. So würde ich eine Timeout-Schleife strukturieren:
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();
quelle
Wenn möglich, lassen Sie die asynchrone Verarbeitung in a einschließen
Task<T>
. Dies bietet das Beste von allen Welten:Task<T>
implementiertIAsyncResult
.Async CTP
; Sie spielen auch gut mitRx
.Wenn Sie ein Timeout verwenden müssen, kann Rx oder das Async CTP dies bereitstellen.
quelle
Ich würde mir die WaitHandle- Klasse ansehen . Insbesondere die ManualResetEvent- Klasse, die wartet, bis das Objekt festgelegt ist. Sie können auch Timeout-Werte dafür angeben und prüfen, ob diese anschließend festgelegt wurden.
// 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(); }
quelle
Ein Anruf bei
Thread.Sleep
Always ist ein aktives Warten, das vermieden werden sollte.Eine Alternative wäre die Verwendung eines Timers. Zur einfacheren Verwendung können Sie dies in eine Klasse einkapseln.
quelle
Ich rate normalerweise davon ab, Ausnahmen zu werfen.
// 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
quelle
Ich denke, Sie sollten AutoResetEvents verwenden. Sie funktionieren hervorragend, wenn Sie darauf warten, dass ein anderer Thread seine Aufgabe beendet
Beispiel:
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(); } }
quelle
So können Sie es machen
System.Threading.Tasks
:Task t = Task.Factory.StartNew( () => { Thread.Sleep(1000); }); if (t.Wait(500)) { Console.WriteLine("Success."); } else { Console.WriteLine("Timeout."); }
Wenn Sie Tasks jedoch aus irgendeinem Grund nicht verwenden können (z.
ManualResetEvent
B. gemäß einer Anforderung von .Net 2.0), können Sie diese wie in der Antwort von JaredPar erwähnt verwenden oder Folgendes verwenden: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); } } }
Mit dem Wait / Pulse-Ansatz erstellen Sie keine expliziten Ereignisse, sodass Sie sich nicht um deren Entsorgung kümmern müssen.
Anwendungsbeispiel:
var rh = new RunHelper( () => { Thread.Sleep(1000); }); if (rh.Wait(500)) { Console.WriteLine("Success."); } else { Console.WriteLine("Timeout."); }
quelle