2011-12-05 22 views
7

Moje pytanie brzmi, w poniższym kodzie, mogę mieć pewność, że metody instancji będą miały dostęp do zmiennych, które moim zdaniem będą, lub czy mogą zostać zmienione przez inny wątek, podczas gdy wciąż pracuję ? Czy zamknięcie ma coś wspólnego z tym, tzn. Czy będę pracował nad lokalną kopią IEnumerable<T>, więc wyliczenie jest bezpieczne?Wielowątkowość, lambda i zmienne lokalne

Parafrazując moje pytanie, czy potrzebuję żadnych zamków, jeśli nigdy nie piszę do wspólnych zmiennych?

public class CustomerClass 
{ 
    private Config cfg = (Config)ConfigurationManager.GetSection("Customer"); 

    public void Run() 
    { 
     var serviceGroups = this.cfg.ServiceDeskGroups.Select(n => n.Group).ToList(); 

     var groupedData = DataReader.GetSourceData().AsEnumerable().GroupBy(n => n.Field<int>("ID")); 
     Parallel.ForEach<IGrouping<int, DataRow>, CustomerDataContext>(
      groupedData, 
      () => new CustomerDataContext(), 
      (g, _, ctx) => 
      { 
       var inter = this.FindOrCreateInteraction(ctx, g.Key); 

       inter.ID = g.Key; 
       inter.Title = g.First().Field<string>("Title"); 

       this.CalculateSomeProperty(ref inter, serviceGroups); 

       return ctx; 
      }, 
      ctx => ctx.SubmitAllChanges()); 
    } 

    private Interaction FindOrCreateInteraction(CustomerDataContext ctx, int ID) 
    { 
     var inter = ctx.Interactions.Where(n => n.Id = ID).SingleOrDefault(); 

     if (inter == null) 
     { 
      inter = new Interaction(); 
      ctx.InsertOnSubmit(inter); 
     } 

     return inter; 
    } 

    private void CalculateSomeProperty(ref Interaction inter, IEnumerable<string> serviceDeskGroups) 
    { 
     // Reads from the List<T> class instance variable. Changes the state of the ref'd object. 
     if (serviceGroups.Contains(inter.Group)) 
     { 
      inter.Ours = true; 
     } 
    } 
} 
+0

Czy możesz udostępnić implementację CustomerDataContext? Wydaje się, że jest to jedyne miejsce, w którym obecnie może się odbywać wyścig danych. –

+0

CustomerDataContext to standardowy obiekt DataContext środowiska Entity, a w rzeczywistości jest to wątek lokalny, więc nie ma tam danych. –

Odpowiedz

3

Wydaje mi się, że znalazłem odpowiedź, a także pytanie.

Prawdziwe pytanie brzmiało, czy lokalne "zmienne", które okażą się rzeczywistymi obiektami, mogą być zaufane przy równoczesnym dostępie. Odpowiedź brzmi: nie, jeśli mają stan wewnętrzny, który nie jest obsługiwany w sposób bezpieczny dla wątków, wszystkie zakłady są wyłączone. Zamknięcie nie pomaga, po prostu przechwytuje odniesienie do wspomnianego obiektu.

W moim konkretnym przypadku - współbieżne odczytuje z IEnumerable<T> i nie pisze do niego, to jest faktycznie wątku bezpieczne, ponieważ każde wywołanie foreach, Contains(), Where() itp dostaje odświeżony IEnumerator, który jest widoczny tylko z wątek, który o to poprosił. Wszelkie inne przedmioty również muszą być sprawdzane, jeden po drugim.

Tak, hura, bez blokady lub zsynchronizowane kolekcje dla mnie :)

Dzięki @ebb i @Dave, chociaż nie bezpośrednio odpowiedzieć na pytanie, to wskazał mnie we właściwym kierunku.


Jeśli jesteś zainteresowany w wynikach, to jest na moim domowym komputerze (quad-core) z Thread.SpinWait symulowania czas przetwarzania wiersza. Prawdziwa aplikacja uzyskała poprawę o prawie 2X (01:03 w stosunku do 00:34) na dwurdzeniowym hiperwątkowym komputerze z programem SQL Server w sieci lokalnej.

Singlethreaded Jednowtykowa, przy użyciu foreach. Nie wiem dlaczego, ale istnieje dość duża liczba przełączników kontekstowych.

Multithreaded Używanie Parallel.ForEach, w razie potrzeby bez blokady, z lokalnymi gwintami.

1

W tej chwili, z tego, co wiem, twoje metody instancji nie używają żadnych zmiennych członkowskich. To sprawia, że ​​stają się bezpaństwowcami, a zatem wątkami bezpieczeństwa. Jednak w tym samym przypadku lepiej byłoby zaznaczyć je jako "statyczne" dla jasności kodu i niewielkiej wydajności.

Jeśli te metody instancji używały zmiennej składowej, to byłyby one tak samo bezpieczne dla wątków jak ta zmienna (na przykład, jeśli użyłbyś prostej listy, nie byłaby ona bezpieczna dla wątków i może pojawić się dziwne zachowanie). Krótko mówiąc, zmienne składowe są wrogiem łatwego bezpieczeństwa wątków.

Oto mój refactor (zrzeczenie się, nie testowane). Jeśli chcesz dostarczyć dane, które zostały przekazane, pozostaniesz bezpieczniejszy, jeśli przekażesz je jako parametry i nie przechowujesz ich jako zmiennych członkowskich:

AKTUALIZACJA: Poprosiłeś o sposób odniesienia się do swojej listy tylko do odczytu, więc Dodałem to i usunąłem tagi statyczne (dzięki czemu można udostępnić zmienną instancji).

public class CustomerClass 
{ 

private List<string> someReadOnlyList; 

    public CustomerClass(){ 
     List<string> tempList = new List<string>() { "string1", "string2" }; 
     someReadOnlyList = ArrayList.Synchronized(tempList); 
    } 

    public void Run() 
    { 
     var groupedData = DataReader.GetSourceData().AsEnumerable().GroupBy(n => n.Field<int>("ID")); 

     Parallel.ForEach<IGrouping<int, DataRow>, CustomerDataContext>(
      groupedData, 
      () => new CustomerDataContext(), 
      (g, _, ctx) => 
      { 
       var inter = FindOrCreateInteraction(ctx, g.Key); 

       inter.ID = g.Key; 
       inter.Title = g.First().Field<string>("Title"); 

       CalculateSomeProperty(ref inter); 

       return ctx; 
      }, 
      ctx => ctx.SubmitAllChanges()); 
    } 

    private Interaction FindOrCreateInteraction(CustomerDataContext ctx, int ID) 
    { 
     var query = ctx.Interactions.Where(n => n.Id = ID); 

     if (query.Any()) 
     { 
      return query.Single(); 
     } 
     else 
     { 
      var inter = new Interaction(); 
      ctx.InsertOnSubmit(inter); 
      return inter; 
     } 
    } 

    private void CalculateSomeProperty(ref Interaction inter) 
    { 
     Console.Writeline(someReadOnlyList[0]); 
     //do some other stuff 
    } 
} 
+0

Dziękuję za odpowiedź :) Jeśli jednak nie piszę do tej listy, tylko czytając z niej? Druga metoda, 'CalculateSomeProperty()' wygląda trochę rzeczy z tego 'List ', jednak nigdy nie dodając lub usuwając z niego. Łatwiej jest go zobaczyć z całej klasy, ponieważ inne metody tego potrzebują. –

+0

@VladislavZorov, 'ReadOnlyCollection' - http://msdn.microsoft.com/en-us/library/ms132474.aspx – ebb

+0

więc wydaje się nawet czytanie' 'Lista (lub' 'ReadOnlyCollection ) nie jest thread- bezpieczna operacja. Jednak nie mogę zrozumieć zdanie "Public static (Shared in Visual Basic) członkowie tego typu są bezpieczne wątku"? Czemu? –