C# yer kontrol istasyonu donuyor, arayüzü akıştan ayır
Kendi yer kontrol istasyonunu C# ile yazdın. Bağlantı kuruluyor, sayılar akıyor, sonra pencere tutulup kalıyor. Fareyi gezdiriyorsun, başlık çubuğu bembeyaz oluyor. Telemetri hattında bir sorun yok; hat çalışıyor, çizen taraf yetişemiyor.
Buradaki tuzak, hattın beklediğinden çok daha hızlı olması. Gelen her pakette arayüzü rahatsız ediyorsun.
Baytlar arayüze nasıl ulaşıyor
Gelen bayt yığını önce çerçevelere ayrılır, sonra bir yere yazılır. Kararı tam bu yazma anında veriyorsun: değeri doğrudan ekrandaki kontrole mi yazacaksın, yoksa arada duran bir alana mı.
4a yolunda okuma işini yapan iş parçacığı, her çerçeveden sonra arayüz iş parçacığına geçmek ve onun sırası gelene kadar beklemek zorunda kalır. Tek bir geçiş ucuzdur, saniyede binlercesi pencereyi kilitler.
Maliyeti çağrının sayısı belirliyor
Bu yazıyı doğrulamak için aynı telemetri akışını iki ayrı tasarımla tükettim. İki koşuda da akış, süre ve çözülen çerçeve sayısı neredeyse aynıydı. Değişen tek şey arayüze kaç kez geçildiği.
Tek bir geçişin ölçülen gidiş-dönüş maliyeti 1,6 mikrosaniyeydi. Saniyede 1363 kez yapıldığında bu, saniyenin küçük bir dilimi gibi görünür ama gerçek arayüz o geçişte hem bekler hem çizer. Çizim işi de eklenince mesaj kuyruğu birikmeye başlar ve pencere yanıt vermez hâle gelir.
Bir ayrıntı burada işleri karıştırıyor: yer kontrol istasyonun otopilota kalp atışı basmıyor ve akış istemiyorsa, otopilot sana saniyede yalnızca birkaç paket gönderir. Aynı koşuda bu fark 5,5 çerçeve/sn ile 1363,8 çerçeve/sn arasındaydı. Yani ilk denemende arayüz gayet akıcı görünür, akışları açtığın gün donar.
Okuma tarafını arayüzden ayır
Çözüm arayüze daha seyrek geçmek. Okuma iş parçacığı kilitli bir alana yazar ve hiçbir kontrole dokunmaz. Ayrı bir döngü sabit hızda uyanır, o alanın anlık kopyasını alır ve ekranı tek seferde tazeler.
Tazeleme hızı 10 Hz olduğunda paket hızı ne olursa olsun arayüze geçiş sayısı sabit kalır. Saniyede on tazeleme, akıcı görünen bir arayüz için yeter.
Doğrulama koşusunun çıktısı
Yukarıdaki tasarımı, bu yazı için yazdığım örnek programda 20 saniye çalıştırdım. Aşağıdaki satırlar o koşunun çıktısıdır, elle yazılmadı.
cozulen cerceve : 29616arayuze gecis : 192gecis / cerceve : 0.65 %son mesaj id : 11030Program yirmi dokuz binden fazla çerçeve çözdü, arayüze yalnızca 192 kez geçti. Oran binde altı buçuk.
Hata Mesajları ve Anlamları
Bu arızayı çoğu zaman hiçbir hata almadan yaşarsın. Ama okuma iş parçacığından kontrole dokunduğun anda karşına çıkabilecek dizeler şunlar.
Cross-thread operation not valid: Control '...' accessed from a thread other than the thread it was created on.
Okuma iş parçacığından bir kontrolün penceresine erişmeye çalıştın. Bu denetimin açık olup olmadığı derleme biçimine değil, süreç başlarken hata ayıklayıcının bağlı olup olmadığına bakar. Yani hata ayıklayıcısız çalıştırdığın sürümde aynı yanlış kod sessizce geçer, arada bir tuhaf davranır.
Invoke or BeginInvoke cannot be called on a control until the window handle has been created.
Form daha ekrana gelmeden arayüze geçiş denendi. Genellikle bağlantıyı kurucu metotta ya da form yüklenmeden başlatılan bir okuma döngüsünde çıkar.
The I/O operation has been aborted because of either a thread exit or an application request.
Okuma sürerken port kapatıldı. Kapatma sırasını tersine çevir: önce döngüyü durdur, bitmesini bekle, sonra portu kapat.
Probable I/O race condition detected while copying memory.
Aynı tampon iki iş parçacığından birden kullanılıyor. Okuma tamponunu tek bir iş parçacığına ait tut.
Devamı kursta
Bu yazı sorunun neye benzediğini ve tasarımın nasıl ayrılacağını anlatıyor. Kuyruk birikmesini kesen sayaç, kapanış sırası, kart bazlı farklar ve gerçek bir yer kontrol istasyonunun kaynak kodundaki karşılıkları Sıfırdan İleri Seviyeye C# eğitiminde adım adım işleniyor.
İNDİRİM KUPONU
Kupon kodu: SOFTIVATION15
Eğitimi alırken ödeme ekranındaki "Kupon kodu" kutusuna bu kodu yaz, %15 iner.




Yorumlar