Saya ingin membuat reservasi segera bahwa kode kami berjalan di lingkungan virtual (mesin) .NET Framework, yang pada gilirannya berjalan pada sistem operasi tujuan umum, jadi kami tidak akan berbicara tentang akurasi apa pun bahkan dalam 1-2 ms. Meskipun demikian, kami akan mencoba melakukan segala daya kami untuk meningkatkan akurasi temporal.
Seringkali dalam program kami, perlu memperbarui beberapa informasi dengan interval waktu tertentu. Dalam kasus saya, ini merupakan pembaruan snapshots (gambar) dari kamera ip. Seringkali, logika bisnis suatu aplikasi menetapkan batas tertentu pada frekuensi pembaruan data. Untuk saat ini adalah 1 detik.
Solusi di dahi adalah menginstal Thread.Sleep (1000) /Task. Tunggu (1000) setelah permintaan snapshot.
static void Getsnapshot() { var rnd = new Random() var sleepMs = rnd.Next(0, 1000); Console.WriteLine($"[{DateTime.Now.ToString("mm:ss.ff")}] DoSomethink {sleepMs} ms"); Thread.Sleep(sleepMs); } while (true) { Getsnapshot(); Thread.Sleep(1000); }
Tetapi jangka waktu operasi kami adalah kuantitas yang tidak ditentukan. Oleh karena itu, simulasi pengambilan foto terlihat seperti ini:
Jalankan program kami dan jalankan output
[15:10.39] DoSomethink 974 ms [15:12.39] DoSomethink 383 ms [15:13.78] DoSomethink 99 ms [15:14.88] DoSomethink 454 ms [15:16.33] DoSomethink 315 ms [15:17.65] DoSomethink 498 ms [15:19.15] DoSomethink 708 ms [15:20.86] DoSomethink 64 ms [15:21.92] DoSomethink 776 ms [15:23.70] DoSomethink 762 ms [15:25.46] DoSomethink 123 ms [15:26.59] DoSomethink 36 ms [15:27.62] DoSomethink 650 ms [15:29.28] DoSomethink 510 ms [15:30.79] DoSomethink 257 ms [15:32.04] DoSomethink 602 ms [15:33.65] DoSomethink 542 ms [15:35.19] DoSomethink 286 ms [15:36.48] DoSomethink 673 ms [15:38.16] DoSomethink 749 ms
Seperti yang kita lihat, lag akan menumpuk dan karenanya logika bisnis aplikasi kita akan dilanggar.
, 60 1 49.
Anda dapat mencoba mengukur jeda rata-rata dari dan mengurangi waktu tidur dengan mengurangi penyimpangan rata-rata, tetapi dalam hal ini kita bisa mendapatkan lebih banyak permintaan daripada yang dibutuhkan oleh logika bisnis kita. Kami tidak akan pernah dapat memprediksi, mengetahui bahwa permintaan diselesaikan hingga 1 detik - berapa banyak milidetik yang perlu kami tunggu untuk memastikan periode pembaruan yang diperlukan.
, 60 1 62.
Solusi yang jelas menunjukkan dirinya. Ukur waktu sebelum dan sesudah operasi. Dan hitung selisihnya.
while (true) { int sleepMs = 1000; var watch = Stopwatch.StartNew(); watch.Start(); Getsnapshot(); watch.Stop(); int needSleepMs = (int)(sleepMs - watch.ElapsedMilliseconds); Thread.Sleep(needSleepMs); }
Jalankan program kami sekarang. Jika Anda beruntung, Anda akan melihat sesuatu seperti yang berikut ini.
[16:57.25] DoSomethink 789 ms [16:58.05] Need sleep 192 ms [16:58.25] DoSomethink 436 ms [16:58.68] Need sleep 564 ms [16:59.25] DoSomethink 810 ms [17:00.06] Need sleep 190 ms [17:00.25] DoSomethink 302 ms [17:00.55] Need sleep 697 ms [17:01.25] DoSomethink 819 ms [17:02.07] Need sleep 181 ms [17:02.25] DoSomethink 872 ms [17:03.13] Need sleep 128 ms [17:03.25] DoSomethink 902 ms [17:04.16] Need sleep 98 ms [17:04.26] DoSomethink 717 ms [17:04.97] Need sleep 282 ms [17:05.26] DoSomethink 14 ms [17:05.27] Need sleep 985 ms
Mengapa saya menulis jika saya beruntung? Karena watch.Start () dijalankan sebelum DoSomethink () dan watch.Stop () setelah DoSomethink (); Operasi-operasi ini tidak instan + lingkungan runtime itu sendiri tidak menjamin keakuratan waktu eksekusi program (x). Karena itu, akan ada overhead. Fungsi DoSomethink () kami berjalan dari 0-1000 ms (y). Oleh karena itu, situasi dapat muncul ketika x + y> 1000 dalam kasus seperti itu
int needSleepMs = (int)(sleepMs - watch.ElapsedMilliseconds);
akan mengambil nilai negatif dan kami akan mendapatkan ArgumentOutOfRangeException karena metode Thread.Sleep () seharusnya tidak mengambil nilai negatif.
Dalam kasus seperti itu, masuk akal untuk mengatur waktu needSleepMs ke 0;
Pada kenyataannya, pada kenyataannya, fungsi DoSomethink () dapat mengeksekusi selama yang Anda inginkan dan kita bisa mendapatkan variabel overflow saat melakukan casting ke int. Lalu waktu tidur kita
dapat melebihi sleepMs;
Anda dapat memperbaikinya sebagai berikut:
var needSleepMs = sleepMs - watch.ElapsedMilliseconds; if (needSleepMs > 0 && watch.ElapsedMilliseconds <= sleepMs) { needSleepMs = (int)needSleepMs; } else { needSleepMs = 0; } Thread.Sleep(needSleepMs);
Pada prinsipnya, semuanya sudah siap. Tetapi menggunakan pendekatan ini bahkan di 1 tempat menyebabkan ketidaknyamanan bagi mata programmer. Dan jika ada lusinan tempat seperti itu dalam program, maka kodenya akan berubah menjadi tumpukan ...
Untuk memperbaikinya, kami merangkum kode kami dalam suatu fungsi. Di sini Anda dapat meletakkannya di kelas yang terpisah atau menggunakan Global sebagai tempat sampah reguler untuk kelas dan menggunakannya sebagai statis (versi saya).
Dalam contoh kita, biarkan itu untuk kesederhanaan, tinggalkan di kelas Program
public static int NeedWaitMs(Action before, int sleepMs) { var watch = Stopwatch.StartNew(); watch.Start(); before(); watch.Stop(); var needSleepMs = sleepMs - watch.ElapsedMilliseconds; if (needSleepMs > 0 && watch.ElapsedMilliseconds <= sleepMs) return (int) needSleepMs; return 0; }
Fungsi input kami menerima tautan ke fungsi yang akan dilakukan dan waktu tunggu yang direncanakan. Dan itu mengembalikan waktu program kita seharusnya tidur.
Untuk kemudahan penggunaan, kami juga dapat meneruskan fungsi lambda anonim ke fungsi kami.
Daftar lengkap program diberikan di bawah ini:
using System; using System.Diagnostics; using System.Threading; namespace ConsoleApp2 { class Program { static void Getsnapshot() { var rnd = new Random(); var sleepMs = rnd.Next(0, 1000); Console.WriteLine($"[{DateTime.Now.ToString("mm:ss.ff")}] DoSomethink {sleepMs} ms"); Thread.Sleep(sleepMs); } static void Main(string[] args) { while (true) { var sleepMs = NeedWaitMs(Getsnapshot, 1000); Console.WriteLine($"[{DateTime.Now.ToString("mm:ss.ff")}] Need sleep {sleepMs} ms {Environment.NewLine}"); Thread.Sleep(sleepMs); } } public static int NeedWaitMs(Action before, int sleepMs) { var watch = Stopwatch.StartNew(); before(); watch.Stop(); var needSleepMs = sleepMs - watch.ElapsedMilliseconds; return needSleepMs > 0 ? (int) needSleepMs : 0; } } }