Подумав немного, я думаю, что знаю, в чем смущение.
Давайте посмотрим на ваш Test
метод?
public static IEnumerable<SimpleClass> Test(this IEnumerable<SimpleClass> sc) =>
sc.AsParallel().Select(s => new SimpleClass
{
OwnNumber = s.OwnNumber,
ActualNumber = s.Apply()
});
.Select(s => {})
<- это «трансформирует» каждую локальную переменную <code>s
И это делает ваш текущий поток кода безопасным без каких-либо проблем с параллелизмом, поскольку s
является local variable
, нет никакого способа, которым другой поток может изменить ссылка на local variable
.
Чтобы ваш код потерпел неудачу, вам понадобится shared variable
, к которому одновременно могут обращаться несколько потоков в любой данный момент времени.
Например, если ваш код выглядел следующим образом:
public static class SimpleClassHelper
{
private static SimpleClass _sharedSimpleClassHelper;
public static int Apply()
{
int z = 1;
for (var i = 0; i < 1000; i++)
{
z += i;
}
return _sharedSimpleClassHelper.ActualNumber; //use the shared variable
}
public static IEnumerable<SimpleClass> Test(this IEnumerable<SimpleClass> sc) =>
sc.AsParallel().Select(s =>
{
_sharedSimpleClassHelper = s; //pass the local variable to a shared variable, that can be referenced from multiple threads
return new SimpleClass
{
OwnNumber = s.OwnNumber,
ActualNumber = Apply()
};
});
}
Вы можете увидеть его в действии здесь: https://dotnetfiddle.net/rF68e2
Редактировать:
Основываясь на вашем комментарии
, но все еще есть некоторая путаница, как метод stati c, который в theroy (по крайней мере, я так думаю) может быть вызван из разных потоков, гарантирует, что если вы вызовете метод с одним объектом, объект останется тем же самым до конца метода. В параллельном программировании обычно типы stati c имеют совершенно противоположную цель - делиться данными между потоками, а не инкапсулировать.
Да, метод stati c, как и любой метод, может быть вызван из разных потоков в одно и то же время, но что решает, является ли метод потокобезопасным, не так, если метод объявлен как stati c или нет, так это если потоки имеют общее состояние или нет.
Давайте еще раз посмотрите на ваш метод применения stati c (убрал для l oop, чтобы было легче смотреть)
public static int Apply(this SimpleClass sc)
{
return sc.ActualNumber;
}
Что заставляет вас верить, что референт c sc
может
sc
- это local variable
, который имеет только область действия этого метода, ничто за пределами этого метода не может дать этой локальной переменной другую ссылку.
Так что даже если Apply
будет вызван x раз в то же время, каждый раз, когда все эти local variable
будут иметь указатель на переданный в классе.
Конечно, если для примера ваш метод выглядел так:
public static int Apply(this SimpleClass sc)
{
var localVariable_ActualNumber = sc.ActualNumber;
localVariable_ActualNumber += 1;
return localVariable_ActualNumber;
}
Этот код я Потокобезопасен, поскольку все потоки обращаются к общей памяти и управляют ею ActualNumber
.
Например:
- Поток 1 читает ActualNumber (1)
- Тема 2 читает ActualNumber (1)
- Тема 1 + = 1
- Тема 1 пишет ActualNumer (2)
- Тема 3 читает ActualNumber (2)
- Тема 3 + = 1
- Тема 3 пишет ActualNumer (3)
- Тема 2 + = 1
- Тема 2 пишет ActualNumer (2)
Vs без потоков
- читает ActualNumber (1)
- + = 1
- пишет ActualNumer (2)
- читает ActualNumber (2)
- + = 1
- пишет ActualNumer (3)
- читает ActualNumber (3)
- + = 1
- пишет ActualNumer (4 )
Таким образом, даже если метод был введен 3 раза, ActualNumber
будет только 2
вместо 4
.
Но вы можете спросить, почему theads теперь делиться памятью?
Потому что local variable
это всего лишь pointer
для один SimpleClass
, если вы обращаетесь к значению внутри SimpleClass
и изменяете его, оно действительно для всех потоков.
Вы также можете спросить себя, считаете ли вы, что любой поток может получить доступ localVariable_ActualNumber
?
Нет, они не могут.
То же самое относится к sc
, к local variable
нельзя получить доступ из другого потока.
Но если вы куда манипулировать sc.ActualNumber
это может вызвать проблемы с безопасностью потоков, поскольку каждый sc
указывает на один и тот же объект в памяти, а ActualNumber
распределяется между потоками.