c# code 39 reader 15: Multithreaded Deadlocks in Visual C#

Creator Code-39 in Visual C# 15: Multithreaded Deadlocks

15: Multithreaded Deadlocks
Code39 Maker In C#.NET
Using Barcode generation for .NET Control to generate, create Code 3/9 image in VS .NET applications.
Code 3/9 Scanner In C#.NET
Using Barcode reader for .NET framework Control to read, scan read, scan image in VS .NET applications.
Overview Without a doubt, the hardest problems to solve in modern software development are multithreaded deadlocks. Even if you think you planned for every situation, your multithreaded application can stop dead when you least expect it. The biggest obstacle to debugging multithreaded deadlocks is that by the time your application is deadlocked, it's almost too late to start debugging. In this chapter, I'll go over some tricks and techniques that have worked for me when I've done multithreaded programming. I'll also present a utility I wrote, DeadlockDetection, that lets you see the flow of events that led up to your deadlock, which is about the only evidence you'll have to help you track down what went wrong and figure out how to avoid the same deadlock in the future. The information in this chapter and the DeadlockDetection utility will help you avoid the minefield of multithreaded deadlocks. Multithreading Tips and Tricks As I've been emphasizing throughout this book, one of the keys to debugging is up-front planning. With multithreaded programming, up-front planning is the only way you can avoid the dreaded deadlocks. I break down the necessary planning for multithreaded applications into the following categories: Don't do it. Don't overdo it. Multithread only small, discrete pieces. Synchronize at the lowest level. Spin your critical sections. Don't use CreateThread. The default memory manager might kill you. Get the dump in the field. Review the code and review the code again. Test on multiprocessor machines. Don't Do It This first tip might seem a little facetious, but I'm absolutely serious. Make sure there's no other way you can structure your program before you decide to incorporate multithreading into your application. When you include multithreading in your application, you're easily adding a minimum of an extra month of development and testing to your schedule. If you're coding thick client applications and you need your program to do some lightweight background processing, check to see whether the work can be handled either through the Microsoft Foundation Class (MFC) library OnIdle processing or through a background periodic timer event. With a little creative thinking, you can probably find a way to avoid multithreading and the headaches that go with it. Don't Overdo It When it comes to server-based applications, you also have to be extremely careful not to create too many threads. One common mistake we've all seen is that some developers end up with a server application in which each connection runs on its own thread. The average development team is doing well to get 10 concurrent connections during their heaviest testing, and it looks like their code works fine. The code might work fine when first 526
Bar Code Drawer In Visual C#.NET
Using Barcode creation for .NET Control to generate, create bar code image in .NET framework applications.
Scan Bar Code In C#
Using Barcode scanner for Visual Studio .NET Control to read, scan read, scan image in VS .NET applications.
deployed, but as soon as business picks up, the server starts bogging down because it's not scalable. When working on server applications, you'll definitely want to take advantage of the excellent support Microsoft Windows 2000, Windows XP, and Windows Server 2003 has for thread pooling with the QueueUserWorkItem family of functions. That way you can finetune the tradeoff between the number of threads and the amount of work you want to get done. Developers are used to things like Microsoft Internet Information Services (IIS) and COM+ handling thread pooling, but developing your own thread pooling system is not something most developers have much experience at, so make sure you spend extra time prototyping your particular situation. For instance, it's much easier to deadlock with misused thread pools than you can ever imagine. Multithread Only Small, Discrete Pieces If you must multithread, try to keep it to small, discrete pieces. With thick client applications, you should stick to small pieces of work that are generally devoid of any user interface (UI) elements. For example, printing in the background is a smart use of multithreading because your application's UI will be able to accept input while data is printing. In server applications, it's slightly different in that you need to judge whether the overhead of thread creation and work will actually speed up your application. Although threads are much more lightweight than processes, they still take quite a bit of overhead. Consequently, you'll want to make sure that the benefit of cranking up that thread will be worth the effort. For example, many server applications have to transfer data back and forth between some type of database. The cost of waiting for the write to that database can potentially be high. If you have a situation in which you don't need to do transactional recording, you can plop parts of the database write into a thread pool object and let it complete on its own time, and thus continue your processing. That way you'll be more responsive to the calling process and get more work done. Synchronize at the Lowest Level Since writing the first edition of this book, I have seen that this particular multithread rule is broken more than any other. You have to keep your synchronization methods at the lowest level possible in your code. This might sound like common sense, but the mistake I see made over and over is that developers are using fancy C++ wrapper classes that grab the synchronization object in the constructor and release it in the destructor. The following code shows an example class you'll find in CRITICALSECTION.H in this book's source code: class CUseCriticalSection; class CCriticalSection { public :
Code 3 Of 9 Printer In .NET
Using Barcode creation for ASP.NET Control to generate, create Code-39 image in ASP.NET applications.
Code-39 Creation In VS .NET
Using Barcode creation for .NET Control to generate, create Code-39 image in VS .NET applications.
CCriticalSection ( DWORD dwSpinCount = 4000 ) { InitializeCriticalSectionAndSpinCount ( &m_CritSec dwSpinCount } ~CCriticalSection ( ) 527 , ) ;
Create Code 39 In VB.NET
Using Barcode encoder for Visual Studio .NET Control to generate, create Code 39 Extended image in Visual Studio .NET applications.
UPC Symbol Generator In Visual C#.NET
Using Barcode drawer for VS .NET Control to generate, create UPC A image in VS .NET applications.
{ DeleteCriticalSection ( &m_CritSec ) ; } friend CUseCriticalSection ; public } ; class CUseCriticalSection { public { m_cs = &cs ; EnterCriticalSection >m_CritSec)); } ~CUseCriticalSection ( ) { LeaveCriticalSection >m_CritSec) ); m_cs = NULL ; } private { m_cs = NULL ; } const CCriticalSection * m_cs ; } ; These classes look great from an object-oriented standpoint, but the implementation issues absolutely kill your performance. The constructor for a wrapper class such as CUseCriticalSection is called at the top of the scope, where it's declared and destroyed when that scope ends. Nearly everyone uses the synchronization class as it is shown in the following code: void DoSomethingMultithreaded ( ) { CUseCriticalSection ( g_lpCS ) ; : ( (LPCRITICAL_SECTION)&(m_cs( ( LPCRITICAL_SECTION)&(m_cs: CUseCriticalSection ( const CCriticalSection & cs ) : CRITICAL_SECTION m_CritSec ;
Generating Bar Code In C#.NET
Using Barcode creation for Visual Studio .NET Control to generate, create barcode image in VS .NET applications.
Encoding Denso QR Bar Code In Visual C#
Using Barcode printer for VS .NET Control to generate, create QR Code image in VS .NET applications.
CUseCriticalSection ( void )
Making PDF-417 2d Barcode In C#.NET
Using Barcode drawer for .NET framework Control to generate, create PDF-417 2d barcode image in .NET applications.
Drawing Universal Product Code Version E In C#
Using Barcode generation for Visual Studio .NET Control to generate, create Universal Product Code version E image in Visual Studio .NET applications.
for ( . . . ) { CallSomeOtherFunction ( . . . ) ; } // Here's the only piece of data really needing protection. m_xFoo = z ; YetAnotherCallHere ( . . . ) ; } The constructor grabs the critical section at the top curly brace, that is, right after the prolog, yet the destructor is not called until the bottom curly brace, right before the epilog. That means you hold onto the critical section for the life of the function, even though DoSomethingMultithreaded is probably calling functions that don't need to be holding onto the critical section. All you are succeeding in doing is killing performance. As you look at DoSomethingMultithreaded, you're probably thinking, "How expensive can acquiring a synchronization object really be " If there's no contention for the synchronization object, the cost is very small. However, with multiple threads, the instant a thread can't acquire a synchronization object, you begin a potentially astronomical cost! Let's start by taking a look at what happens when you call WaitForSingleObject to acquire a synchronization object. Since you are an assembly language demigod from reading 7, you might want to follow along in the Disassembly window as it will show you exactly what I'm about to discuss. Note that I'm doing the work on Windows XP; the Windows 2000 version of WaitForSingleObject might be slightly different. WaitForSingleObject itself is simply a wrapper around WaitForSingleObjectEx, which does about 40 lines or so of assembly language and calls two functions to set up some data. Down toward the bottom of WaitForSingleObjectEx is a call to NtWaitForSingleObject from NTDLL.DLL. So the WaitForSingleObject function is a call to a wrapper of a wrapper. If you disassemble the address where NtWaitForSingleObject is in memory (use {,,ntdll}_NtWaitForSingleObject@12 in the Address field of the Disassembly window), you'll see that it's really a call to some weird function, ZwWaitForSingleObject, which is also out of NTDLL.DLL. (On Windows 2000, you'll stop at NtWaitForSingleObject.) As you look at the disassembly for ZwWaitForSingleObject, you'll see that it looks something like the following: _ZwWaitForSingleObject@12: 77F7F4A3 77F7F4A8 77F7F4AD 77F7F4AF 77F7F4B2 mov mov call ret nop eax,10Fh edx,7FFE0300h edx 0Ch
Generate USS-128 In Visual Studio .NET
Using Barcode generation for .NET Control to generate, create EAN / UCC - 13 image in VS .NET applications.
Print PDF-417 2d Barcode In None
Using Barcode creator for Office Word Control to generate, create PDF 417 image in Word applications.
The real action is at that address, 0x7FFE0300. If you dump what's at that address, you'll see the following: 7FFE0300 mov edx,esp 7FFE0302 sysenter 529
Make Code 39 Full ASCII In Objective-C
Using Barcode encoder for iPad Control to generate, create ANSI/AIM Code 39 image in iPad applications.
Code 39 Generator In None
Using Barcode drawer for Font Control to generate, create Code39 image in Font applications.
Bar Code Generation In VS .NET
Using Barcode encoder for ASP.NET Control to generate, create bar code image in ASP.NET applications.
Read EAN 13 In Java
Using Barcode scanner for Java Control to read, scan read, scan image in Java applications.
PDF417 Scanner In C#.NET
Using Barcode recognizer for VS .NET Control to read, scan read, scan image in .NET framework applications.
Linear Printer In Visual Basic .NET
Using Barcode creation for .NET framework Control to generate, create 1D Barcode image in .NET applications.
Copyright © OnBarcode.com . All rights reserved.