Вызывать один и тот же метод из разных потоков - PullRequest
3 голосов
/ 24 сентября 2011

Например, у нас есть 5 запущенных потоков. Каждый из них вызывает один и тот же метод. Когда методы будут вызваны, он будет запущен в текущем потоке? Это означает, что один и тот же метод будет запускаться отдельно в разных потоках в (относительно) одно и то же время?

Пример:

public string GetHhmlCode(string url)
{
// ... Some code here
return html;
}

Если мы вызовем этот метод из разных потоков с разными параметрами в одно и то же время, результат будет возвращен соответствующему потоку, что означает, что код выполняется отдельно в разных потоках?

Ответы [ 2 ]

7 голосов
/ 24 сентября 2011

Короткий ответ на первый вопрос: да! Метод будет выполняться несколькими потоками одновременно.

Ответ на второй вопрос: да (с оговорками)! Если вы входите из заданного контекста потока, то метод вернется к тому же контексту потока, и все шаги обычно будут вести себя так, как будто другой поток не существует. Однако, это быстро изменится, если потоки должны читать и / или записывать в одну и ту же переменную. Рассмотрим следующую ситуацию:

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();
    }
}

Предположим, что требуется, чтобы клиент был подключен для успешной выборки страницы, затем рассмотрите следующий порядок выполнения:

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

Поток 2 завершился правильно, но поток 1, возможно, вызвал исключение, и это может вызвать другие проблемы в вашем коде. Обратите внимание, что чтение / запись в сам флаг будет безопасным (то есть эти операции являются атомарными и видимыми для всех потоков из-за того, что флаг помечен как энергозависимый), однако существует условие состязания, поскольку флаг может быть установлен ПОСЛЕ того, как поток уже имеет оценил это. В этом случае существует несколько способов защиты от проблемы:

  1. Использовать блок синхронизации (что-то вроде lock(syncObject){...})
  2. Создайте отдельный http-клиент для каждого потока (возможно, более эффективный и избегающий синхронизации).

Теперь давайте посмотрим на другой пример:

public string GetHtmlCode(string url)
{
    string html = string.Empty;
    string schema = GetSchema(url);
    if(schema.Equals("http://"))
    {
        // get the html code
    }
    return html;
}

Предположим, что происходит следующее:

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

В этом случае оба потока вводят метод с разными параметрами, и только поток 1 вводится с параметром, который может позволить ему извлекать страницу, но поток 2 не мешает потоку 1. Причина этого заключается в том, что потоки не разделяйте общих переменных. Отдельный экземпляр параметра url передается, и каждый поток также получает свой собственный локальный экземпляр схемы (т. Е. Схема не является общей, поскольку она существует только в контексте вызывающего потока).

1 голос
/ 24 сентября 2011

пометить этот метод как статический и не ссылаться на внешние переменные внутри этого метода.тогда все будет работать нормально.

...