The short answer is to the first question is: Yes! The method will be executed by multiple threads concurrently.
The answer to the second question is: Yes (with reservations)! If you enter from a given thread context, then the method will return to the same thread context and all the treads will generally behave as if the other thread doesn't exist. However, this will quickly change if the threads have to read and/or write on the same variable. Consider this situation:
class HttpClient { private volatile bool _httpClientConnected; //.. initialize in constructor public string GetHtmlCode(string url) { string html = string.Empty; if(_httpClientConnected) { html = FetchPage(url); } return html; } public void Connect() { _httpClientConnected = true; ConnectClient(); } public void Disconnect() { _httpClientConnected = false; DisconnectClient(); } }
Suppose it is required that the client is connected for a page to be successfully fetched, then consider this order of execution:
Thread 1: calls GetHtmlCode Thread 1: initialize local html variable Thread 3: calls Disconnect() Therad 2: calls GetHtmlCode Thread 2: initialize local html variable Thread 1: evaluate _httpClientConnected flag Thread 3: sets _httpClientConnected to false Therad 3: calls DisconnectClient() and successfully disconnects THread 3: exits the Disconnect() method. Thread 1: calls FetchPage() Therad 2: evaluates _httpClientConnected flag Thread 2: returns empty html string Therad 1: catches an exception because it attempted to fetch a page when the client was disconnected
Thread 2 exited correctly, but Thread 1 possibly threw an exception and it may cause other problems in your code. Note that read/writing to the flag itself will be safe (i.e. those operations are atomic and visible to all threads due to the flag being labeled as volatile), however there is a race condition because the flag can be set AFTER a thread has already evaluated it. In this case there are a couple of ways to guard against the problem:
- Use a synchronization block (something like
lock(syncObject){...}) - Create a separate http client for each thread (probably more efficient and it avoids synchronization).
Now let's look at another example:
public string GetHtmlCode(string url) { string html = string.Empty; string schema = GetSchema(url); if(schema.Equals("http://")) { // get the html code } return html; }
Suppose the following happens:
Thread 1: calls GetHtmlCode("http://www.abc.com/"); Thread 1: html is assigned an empty string Thread 2: calls GetHtmlCode("ftp://www.xyz.com/"); Thread 2: html is assigned an empty string Therad 1: assigns it's schema to the schema variable Thread 2: assigns it's schema to the schema varaible Thread 2: evaluates schema.Equals("http://") Thread 1: evaluates schema.Equals("http://") Thread 1: fetches html code Therad 2: returns html Therad 1: returns html
In this case both threads entered the method with different parameters and only Thread 1 entered with a parameter that could allow it to fetch a page, but Thread 2 did not interfere with Thread 1. The reason why this happens is because the threads don't share any common variables. A separate instance of the url parameter is passed in and each thread also gets its own local instance of schema (i.e. the schema is not shared because it only exists in the context of the calling thread).