2015-09-01 2 views
1

Я пишу программу Winform протестировать параллельный словарь C# с ниже класса:valueFactory в Параллельное словаре

public class Class1 
{ 
    public int X = 10; 

    public Class1(int x) 
    { 
     X = x; 
     Debug.WriteLine("Class1 Created"); 
    } 
} 

и ниже кнопки код:

private void button1_Click(object sender, EventArgs e) 
    { 
     var dict = new ConcurrentDictionary<int, Class1>(); 

     Func<Class1> valueFactory =() => 
     { 
      Debug.WriteLine("Factory Called"); 
      return new Class1(5); 
     }; 

     var temp = dict.GetOrAdd(1, valueFactory()); 
     Debug.WriteLine(temp.X); 
     temp.X = 20; 

     var temp2 = dict.GetOrAdd(1, valueFactory()); 
     Debug.WriteLine(temp2.X); 
    } 

я заметил, что метод valueFactory имеет всегда выполнялся, и конструктор Class1 вызывался дважды, даже ключ уже существует в dict после первого метода GetorAdd.

Однако, если изменить определение Func к

Func<int, Class1> valueFactory = (k) => 
     { 
      Debug.WriteLine("Factory Called"); 
      return new Class1(5); 
     }; 

и вызвать метод GetorAdd, как показано ниже:

var temp = dict.GetOrAdd(1, valueFactory); 

Программа работает желаемым образом, как это не называют Class1 конструктор в второй вызов. Я подозреваю, что это потому, что я передал делегат valueFactory вместо вызова функции valueFactory() методу GetorAdd. Интересно, есть ли подробное объяснение в том, что происходит под капотом, и я также не понимаю, почему я не могу передать значение valueFactory в качестве делегата, если мое определение Func - это ничего, кроме Func<int, Class1 (то же определение, что и словарь)

спасибо.

ответ

2

Когда вы делаете:

var temp = dict.GetOrAdd(1, valueFactory()); 
... 
var temp2 = dict.GetOrAdd(1, valueFactory()); 

Вы на самом деле применение valueFactoryперед тем вызова dict.GetOrAdd(). Так что это нормально, что он вызывает каждый раз. Конечно, новый экземпляр Class1, возвращенный 2-м вызовом в valueFactory(), в конечном счете не пригодится, но тем не менее он создан.

В отличие от этого, когда вы делаете:

var temp = dict.GetOrAdd(1, valueFactory); 
... 
var temp2 = dict.GetOrAdd(1, valueFactory); 

... вы фактически используете другую перегрузку метода GetOrAdd, где вы передаете ссылку на делегат valueFactory без вызова его самостоятельно. Затем метод GetOrAdd() решает, нужно ли ему вызывать valueFactory или нет, основываясь на том, найден ли ключ в словаре.

ConcurrentDictionary.GetOrAdd Method (TKey, Func) документ:

Добавляет ключ/значение пары к ConcurrentDictionary<TKey, TValue>, используя указанную функцию, , если ключ не существует.

В этом случае не нужно ссылаться на его 2-й раз, потому что он находит ключ 2-й раз, вы называете GetOrAdd().


Однако, обратите внимание, что передача в valueFactory в качестве делегата GetOrAdd не гарантирует, что он не будет вызван дважды.Это особенно верно в многопоточных сценариях. Обратите внимание на то, что документация по ConcurrentDictionary.GetOrAdd Method (TKey, Func) также говорит об этом:

Если вы звоните GetOrAdd одновременно в разных потоках, addValueFactory может быть вызван несколько раз, но пара ключ/значение не может быть добавлено в словарь для каждого вызова.

Смежные вопросы