21

I had an interview just 5 minutes back, I didn't answer 3 questions, could someone please help me.

Question:

How to look for deadlock scenarios in Multithreaded application function and prevent it ?

Answer I gave:

I gave definition of deadlock and lock, mutex, monitor, semaphore. He told me, that these are tools, but how to look for a deadlock scenario as because when we use these tools blindly, it costs the performance he said :(

Please help me understand this.

Jasmine
  • 5,186
  • 16
  • 62
  • 114
  • 3
    You would have to analyze locking patterns... Can't think of a very compact answer. – H H Mar 12 '13 at 12:57
  • 2
    He was asking for a generic way to check for a deadlock? All I know is by looking at the code and using some intelligence... – TheKingDave Mar 12 '13 at 12:58
  • To prevent deadlocks, you have to use a lockfree implementation. If you absolutely need to keep the locks, you have to ensure that whenever multiple locks are required, the objects you lock are locked in a well-defined order. – Nolonar Mar 12 '13 at 12:58
  • Thank you all, I really dont know how to implement these stuffs, I am new :( These all looks scary to me, however I should understand it by some videos :( – Jasmine Mar 12 '13 at 13:02
  • @TheKingDave: Thank you, I am very new :( He was asking too much then as Henk said, it cannot be answered in compact :( Honestly when you folks itself say that its not very easy but doable, how come he could ask me this big question :( ahhh !! I will look into some resource to understand the concept of multithreading. Thank you again :) Cheers – Jasmine Mar 12 '13 at 13:04
  • @Divine This is a pretty standard interview question for .NET systems. You gave a basic answer but didn't tie it into how to solve the problem. [Here](http://www.mindfiresolutions.com/A-single-step-solution-to-avoid-Deadlock-in-Multithreading-414.php) is a simple article describing the problem and a solution. – Gaʀʀʏ Mar 12 '13 at 13:08
  • @Garreh I don't think that will always sort the situation what does the thread do if it does not acquire the lock ? I can think of many examples where all the threads need to run for example in the example the thread then cannot finish as it cannot enter the critical section ... – TheKingDave Mar 12 '13 at 13:17
  • @Garreh: Thank you I will go through that link :( Honestly some of the discussions looks green and latin to me as I dont know anything about multithreading :( I need to understand that first. :( I was curious how I should answer such questions in interview :( Thank you Garreh and others :( – Jasmine Mar 12 '13 at 13:22
  • When your application hangs, press "break debugging" and switch `Debug>Windows>Threads`. With this window you can check all your threads. Normaly you see there a `WaitOne` call or so. – CodeTherapist Mar 12 '13 at 13:24

4 Answers4

13

It sounds like you had problems explaining how deadlocks can occur and how they can be prevented.

A deadlock occurs when each thread (minimum of two) tries to acquire a lock on a resource already locked by another. Thread 1 locked on Resources 1 tries to acquire a lock on Resource 2. At the same time, Thread 2 has a lock on Resource 2 and it tries to acquire a lock on Resource 1. Two threads never give up their locks, hence the deadlock occurs.

The simplest way to avoid deadlock is to use a timeout value. The Monitor class (system.Threading.Monitor) can set a timeout during acquiring a lock.

Example

try{
    if(Monitor.TryEnter(this, 500))
    {
        // critical section
    }
}
catch (Exception ex)
{

}
finally
{
    Monitor.Exit();
}

Read More

Gaʀʀʏ
  • 4,372
  • 3
  • 39
  • 59
5

Performance analysis tools can be also helpful in identifying deadlocks, among others. This question will provide some insight in this topic: C#/.NET analysis tool to find race conditions/deadlocks .

Visual analysis of the code and the proper using of locks is useful also (you should be able to detect potential problems in code while inspecting it), but can be very difficult for complex applications. Sometimes the deadlocks are visible only when you run the code, not simply by inspecting the code.

I don't know too much of your interviewer. Some may want to see how much you know of locking standards/guideliness, some may want to see if you know how to use your tools, some may want both. In the company I work at, for example, the use of tools (expecially the ones we already own and use) is highly appreciated. But that does not imply one should not have the skills that would prevent coding deadlocks in the first place.

Locking something just for the sake of locking affects performance, as thread wait for each other. You have to analyse the workflow to determine what really needs to be locked, when, with what type of lock (simple lock or maybe a ReaderWriterLockSlim). There are many typical ways to prevent deadlock.

For example when using ReaderWriterLockSlim you can use a timeout to prevent deadlocks (if you wait to much, you abort aquiring the lock)

if (cacheLock.TryEnterWriteLock(timeout))
{
...
}

And you should be able to suggest such timeouts.

At such a question I would expect at least a mention of the classic case of deadlocks, like badly use of nested locks (you should be able to know can you avoid them, why they are bad, etc.).

The subject is very big... you can go on and on about this. But without definitions. Knowing what a lock is and knowing to use locks/semaphores/mutex in large scale multi-threading applications are 2 different things.

John Smith
  • 3,863
  • 3
  • 24
  • 42
Coral Doe
  • 1,925
  • 3
  • 19
  • 36
4

I think the interview is asking you a trick question. If you could use static analysis to prevent deadlock... nobody would have deadlock!

Personally, when I look for deadlock I start by finding functions where the critical section spans more than the function call. For example

void func(){
    lock(_lock){
        func2();
     }
}

It's not really clear what func2 is doing. Maybe it dispatches an event on the same thread, which would mean that event is still part of the critical section. Maybe it then locks on a different lock. Maybe it dispatches into the threadpool and no longer is re-entrant because its now on a different thread! These kinds of places are where you can start to see deadlock scenarios happening: when you have multiple non-reentrant lock locations.

Other times, when tracing deadlock scenarios, I backtrack and I try and find where are all the threads created. I give thought to each function and where it can actually be running. If you aren't sure, adding in logging to log where the function call came from can also help.

You can also avoid deadlocks by using lock free data structures (but those require just as much though to use). You want to minimize your access to the lock free structure because each time you access it it can change.

As mentioned in another answer, you can use mutexes with timeouts, but that isn't guaranteed to always work (what if your code needs to work longer than the timeout?). It was mentioned in another comment that this is maybe what the interviewer was asking for. I find that in production this isn't really a good idea. Timeouts vary all the time, maybe something took longer than expected to run and hit a timeout. I think its better to let it deadlock, take a process dump, then find exactly what was holding the locks and fix the problem. Of course, if your business requirements can't allow that, then you can use this as part of a defensive coding strategy along with smart lock placement choices.

I don't agree with your interview that locks always add a huge performance problem. Uncontended locks/mutexes/etc first test as a spinlock before handing off to the OS and spinlocks are cheap.

In general, the best way to avoid deadlock is to understand your program flow. Each time you introduce a new lock object think about where it is used and what uses it down the chain.

devshorts
  • 8,572
  • 4
  • 50
  • 73
1

The simplest solution for this problem would be to always sleep/wait with a big enough timeout. If that timeout happens you know that something took way longer than it should have and you have a good chance of a deadlock or another bug.

Mutex m; // similar overloads exist for all locking primitives
if (!m.WaitOne(TimeSpan.FromSeconds(30)))
    throw new Exception("Potential deadlock detected.");

When WaitOne returns false it will have waited for 30 seconds and the lock still would not have been released. If you know that all locked operations should complete within milliseconds (if not, then just increase the timeout), then this is a very good indication that something went badly.

Knaģis
  • 20,827
  • 7
  • 66
  • 80
  • How does it tell the difference between deadlock and starvation? – Nolonar Mar 12 '13 at 13:08
  • Hi Hnagis, thank you for the answer, I am afraid I didn't understand what you say :( I am new to multithreading :( I am sorry, it will be great if you can rephrase what you are trying to say :( – Jasmine Mar 12 '13 at 13:09
  • 1
    He's setting a timeout on a mutex. This means that it will wait for up to 30 seconds before continuing on. If it times out the function will return false and so he can know that nobody pulsed the mutex. With a sufficiently long timeout this *could* mean deadlock. Could is the keyword here. It doesn't always mean deadlock. – devshorts Mar 12 '13 at 13:10
  • Is there a need to tell the difference? Starvation applies not to all scenarios and when it does you might want to consider that as a bug as well.. This is not an ideal scenario but if one could be invented for every .NET locking mechanism then it would have been built-in by default. – Knaģis Mar 12 '13 at 13:11
  • I think @Knaģis has a neat solution, but I dont' think it is applicable everywhere. You need to understand your program flow and expected delays to make this kind of assumption. – devshorts Mar 12 '13 at 13:12