2015-01-22 14 views
14

Çok kafam karıştı ve debugger'ın bana yalan söylediğini düşünüyorum. Benim kodunda aşağıdaki döngü var:Sıfırla ne zaman bir bölme sıfır değil mi? Hata ayıklayıcısında bir bulmaca (statik değişken sorunları)

MyClass::UploadFile(CString strFile) 
{ 
    ... 
    static DWORD dwLockWaitTime = EngKey::GetDWORD(DNENG_SERVER_UPLOAD_LOCK_WAIT_TIME, DNENG_SERVER_UPLOAD_LOCK_WAIT_TIME_DEFAULT); 
    static DWORD dwLockPollInterval = EngKey::GetDWORD(DNENG_SERVER_UPLOAD_LOCK_POLL_INTERVAL, DNENG_SERVER_UPLOAD_LOCK_POLL_INTERVAL_DEFAULT); 

    LONGLONG llReturnedOffset(0LL); 
    BOOL bLocked(FALSE); 
    for (DWORD sanity = 0; (sanity == 0 || status == RESUMABLE_FILE_LOCKED) && sanity < (dwLockWaitTime/dwLockPollInterval); sanity++) 
    { 
     ... 

Bu döngü benim program süresince yüzlerce kez idam edilmiş ve iki statik değişkenler kodunda yerde değişmez, onlar sadece bir kez yazılır ediyoruz Statik olarak başlatıldığında ve döngü koşullarında ve başka bir yerde okunduğunda. Windows kayıt defterinden okunan kullanıcı ayarları oldukları için, neredeyse her zaman dwLockWaitTime = 60 ve dwLockPollInterval = 5 değerlerine sahip olurlar. Yani döngü her zaman 60/5 yapıyor.

Çok nadiren, çökme yaşıyorum Bu kod satırının sıfır hatasıyla bir bölümü attığını gösteren dökümü. WinDbg ne diyor ben kontrol ettik ve gösterir:

FAULTING_IP: 
procname!CServerAgent::ResumableUpload+54a [serveragent.cpp @ 725] 
00000001`3f72d74a f73570151c00 div  eax,dword ptr [proc!dwLockPollInterval (00000001`3f8eecc0)] 

EXCEPTION_RECORD: ffffffffffffffff -- (.exr 0xffffffffffffffff) 
ExceptionAddress: 000000013f72d74a (proc!CServerAgent::ResumableUpload+0x000000000000054a) 
    ExceptionCode: c0000094 (Integer divide-by-zero) 
    ExceptionFlags: 00000000 
NumberParameters: 0 

ERROR_CODE: (NTSTATUS) 0xc0000094 - {EXCEPTION} Integer division by zero. 

ben montajcı kodunu kontrol ettik ve çarpışma bu div talimatı üzerine gerçekleştiğini gösterir.

00000001`3f72d744 8b0572151c00 mov  eax,dword ptr [dwLockWaitTime (00000001`3f8eecbc)] 
00000001`3f72d74a f73570151c00 div  eax,dword ptr [dwLockPollInterval (00000001`3f8eecc0)] 

Yani 000000013f8eecbc de değer eax içine taşındı ve daha sonra eax000000013f8eecc0 de değeri ile bölünmüştür görebilirsiniz.

Sorduğunuz iki değer nedir? Ben beklediğiniz tam olarak

0:048> dd 00000001`3f8eecbc 
00000001`3f8eecbc 0000003c 00000005 00000001 00000000 
00000001`3f8eeccc 00000000 00000002 00000000 00000000 
00000001`3f8eecdc 00000000 7fffffff a9ad25cf 7fffffff 
00000001`3f8eecec a9ad25cf 00000000 00000000 00000000 
00000001`3f8eecfc 00000000 00000000 00000000 00000000 
00000001`3f8eed0c 00000000 00000000 00000000 00000000 
00000001`3f8eed1c 00000000 00000000 00000000 00000000 
00000001`3f8eed2c 00000000 00000000 00000000 00000000 
0:048> dd 000000013f8eecc0 
00000001`3f8eecc0 00000005 00000001 00000000 00000000 
00000001`3f8eecd0 00000002 00000000 00000000 00000000 
00000001`3f8eece0 7fffffff a9ad25cf 7fffffff a9ad25cf 
00000001`3f8eecf0 00000000 00000000 00000000 00000000 
00000001`3f8eed00 00000000 00000000 00000000 00000000 
00000001`3f8eed10 00000000 00000000 00000000 00000000 
00000001`3f8eed20 00000000 00000000 00000000 00000000 
00000001`3f8eed30 00000000 00000000 00000000 00000000 

sabitleri 60 ve 5. Yani sıfıra göre nerede ??? Hata ayıklayıcım yalan mı? Elbette sıfıra bölme donanım tarafından atılmış, bu yüzden bu konuda bir hata yapamaz mı? Ve eğer kodumda farklı bir yerde sıfıra bölünmüş olsaydı, hata ayıklayıcının yönerge göstergesini tam olarak bu yerde gösterme ihtimali nedir? İtiraf ediyorum, güvendeyim ..

+4

Minidump'ta beklenen değeri görmeniz, yönerge çalıştırıldığında beklenen değerin olduğunu kanıtlamaz. Örneğin, bir iplik geçirme yarışının oldukça klasik davranışı, bir minidump, istisnanın atıldığı anda kaydedilen anlık bir anlık görüntü değildir. Yumuşak RAM hataları, bu tür açıklanamayan aksiliklerin başka bir kaynağıdır. Dolayısıyla, iş parçacığının bir faktör olmadığından eminseniz, müşteriye verilecek en iyi tavsiye, RAM modüllerini değiştirmektir. –

+0

Döngüyü, çağrılabilen bazı küresel kurucunun bir parçası olarak çağırırsanız, iki değişkenin gerekli değere (ve muhtemelen varsayılan olarak çalıştırılabilir yürütülebilir yükleyici tarafından varsayılan olarak "0" olarak başlatılana) bölünmesiyle sonuçlanabilir. derleyicinizin önyükleme koduna "ana()' den önce ihtiyaç duyulur. – inetknght

+3

Yani bu düşünce çizgisi ile ilgili iki nokta. Çöpte ne varsa (miniden ziyade tam bir çöplük) doğru değilse, diğer yoldan ziyade idam edilene göre tarih dışı kalmalıdır. Yani, eğer bu iki değer bozulursa, bir şey onları daha sonra beklenen değerlere geri götürmek zorunda kalırdı ... – Benj

cevap

10

nasıl oldu düşünerek uyku kaybedersem ve birden çok iş parçacığı bu işlevi aradığınız, static değişkenler evreli olmayan bir derleyici kullanıyorsanız Bu, C++ 11 standartlarına uygun değildir. Böylece, bu iki statik değişken başlatılırken veri yarışları alabilirsiniz.

C++ 11 standart uyumlu bir derleyici için, statik iş parçacıklarının artık ilk iş parçacığı tarafından başlatılacağı garanti edilir, sonraki iş parçacıkları ise statik başlatılana kadar bekler.

Visual Studio 2010 ve altı için, bu derleyiciler C++ 03 ve C++ 98 standardına uygun olduğundan, statik yerel değişkenlerin iş parçacığı için güvenli oldukları garanti edilmez.

Visual Studio 2013 için, statik yerel başlatma açısından C++ 11 desteğinin düzeyinden emin değilim. Bu nedenle, Visual Studio 2013 için, statik yerel değişkenlerin doğru şekilde başlatıldığından emin olmak için doğru eşitleme kullanmanız gerekebilir.

Visual Studio 2015 için, bu öğe ele alınmış ve düzgün statik başlatma işlemi tam olarak uygulanmıştır, bu nedenle şu anda sahip olduğunuz kod VS 2015 ve üstü için doğru şekilde çalışmalıdır.


Düzenleme: Visual Studio 2013 için, statik yerel evreli başlatma, as described here ("Sihirli Statiği") uygulanmamıştır. Bu nedenle, özgün sorunun nedeninin statik yerel başlatma sorunu ve iş parçacığı olduğunu doğrulayabiliriz. Üste | Çözüm (eğer VS 2013 ile devam etmek istiyorsanız), doğru senkronizasyon kullanmak ya da statik değişkenlere artık ihtiyaç duyulmaması için uygulamanızı yeniden tasarlamaktır.

+0

Studio Studio 2010'unuz var mı ve bir SSCCE yazıp ikili bir yere mesaj gönderebilir misiniz? –

+0

VS 2008 ve VS 2013'ü kullanıyorum. VS 2010 üzerinden atladım. Ancak, bu derleyiciler C++ 03 standartlarına (ve aşağısına) uyduklarından, statik iş parçacığının, iş parçacığı güvenli bir şekilde birden fazla iş parçacığı tarafından başlatıldığına dair bir garanti yoktur.Microsoft'un Visual Studio 2015'te nihayet düzeltilmiş olduğunu kabul ettiği düşünüldüğünde, VS 2010'da statik yerel değişkenler kullanan ve bu kodun birden çok iş parçacığı tarafından çağrıldığı herhangi bir kodun iş parçacığı için güvenli olmadığını varsayabiliriz. – PaulMcKenzie

+0

Doğru olduğuna şüphe duymuyorum, sadece kendim hata ayıklamak istiyorum ve belki de başkalarına daha ayrıntılı olarak hata ayıklamayı öğretiyorum, ör. montaj koduna bakarak. Ekspres Sürümü ile yapılabilir sanırım, bu yüzden bunun bir kopyasını arayacağım. –

7

Sorun, çoklu okuma ile ilgili olabilir.

  1. bir iplik gizli başlatma zaten
  2. var 0'dır yapılmıştır eğer kayıt okuma değişkeni 1 ve gelirleri ayarlar böylece, görmek statik değişkeni "is_initialized" fonksiyonunu
  3. Çekler girer Bu noktada
  4. başka bir evre başlatıldı olarak
  5. ikinci iplik değişkenleri görür işlevi girer ve başlatma kodunu
  6. bölme gerçekleştirildiğinde denomina atlar tor hala 0 (ilk iş parçacığı hala kayıt defterini okuyor)
  7. Program çöküyor, ancak bu arada, ilk iş parçacığı, dökümde gördüğünüz değişkenleri ayarlayarak yürütmeyi tamamlıyor.
  8. Kodun üye işlevinin bir parçası olduğundan imkansız
+0

Yalnızca çok işlemcili bir CPU üzerinde gerçekleşebileceğini ve iş parçacığı zamanlaması nedeniyle tek çekirdekli bir CPU'da gerçekleşemeyeceğini doğru bir şekilde anlıyor musunuz? Öyleyse, programın benzeşimini tek bir CPU'ya ayarlayabilir miydi? –

+0

@ThomasW: Kayıt defterinden okumak potansiyel olarak çok uzun bir işlemdir. Tek çekirdekli bir işlemci bile, ilk iş parçacığı için zaman dilimini sonlandıramayacak (pencerelerdeki 20ms IIRC) ve sorunu tetikleyen ikinci ipliğin programlanacağı imkansız değildir. – 6502

+0

Evet, sorun hala devam edecek, ancak tek bir CPU'da, hata ayıklayıcının doğru değeri göstermesi için bu işlemi durdurmaz (bu durumda 0)? –

İlgili konular