Я - новичок поточной обработки, и я пытаюсь записать пользовательский ориентированный на многопотоковое исполнение универсальный класс списка в C# (.NET 3,5 SP1). Я читал, Почему ориентированные на многопотоковое исполнение наборы настолько трудно?. После рассмотрения требований класса я думаю, что только должен безопасно добавить к списку и возвратить список. Пример показывает в значительной степени все, что я хочу кроме него, испытывает недостаток в методе списка возврата поэтому, я записал свой собственный открытый метод как ниже:
Обновление: на основе предложений, учитывая я рассмотрел свои требования и поэтому упростил класс до как указано ниже:
public sealed class ThreadSafeList
{
private readonly IList list = new List();
private readonly object lockable = new object();
public void Add(T t)
{
lock (lockable)
{
list.Add(t);
}
}
public IList GetSnapshot()
{
IList result;
lock (lockable)
{
result = new List(list);
}
return result;
}
}
Согласитесь с @jrista. Необходимо решить проблему семантики, и почему она называется Translate ()
? Какова цель?
return new ReadOnlyCollection<T>(list);
У вас все еще есть проблемы с потоками, если исходный список изменяется, если другой поток выполняет итерацию по списку. Пока вы об этом знаете, это не большая проблема.
return new List<T>(list).AsReadOnly();
Этот список не имеет проблем с потоками, потому что ничто не изменяет новый список. Единственная ссылка содержится в оболочке ReadOnlyCollection
.
return new List<T>(list);
Возвращает новый список, и вызывающий может делать со своим списком все, что они хотят, не затрагивая исходный список, и изменения в исходном списке не влияют на этот список.
Имеет ли значение, если другой потребитель берет копию списка и затем изменяет свою копию? Нужно ли потребителям видеть изменения в списке? Вам просто нужен поточно-безопасный перечислитель?
public IEnumerator<T> ThreadSafeEnumerator()
{
List<T> copy;
lock(lockable)
copy = new List<T>(list);
foreach (var value in copy)
yield return value;
}
По моему опыту, вы должны использовать ваш мозг, когда дело касается безопасности потоков и не полагается на такие решения, как эти. короче говоря, это зависит от того, что получатель списка собирается с ним делать.
Если вы хотите загрузить ресурсы из того же протокола, с которым загружена страница, то ее использование является идеальным способом ее выполнения. Однако вам может понадобиться загрузить некоторые ресурсы с http
, даже если ваша страница в настоящее время обслуживается в https
(допустим, ресурс обслуживается только на http
или вы предпочитаете уменьшить нагрузку на ваш сервер, не заставляя его шифровать каждый образ на странице). В этом случае необходимо явно указать имя протокола.
Делает ByteArrayOutputStream.close () действительно освободить память?
Нет. Абсолютно ничего не делает. Вы можете посмотреть на его исходный код:
public void close() throws IOException {
}
Чтобы освободить память, убедитесь, что нет никаких ссылок на нее и пусть Garbage Collector делает свою вещь. Как и с любым другим нормальным объектом.
Потоки на основе файлов и сокетов являются особыми, поскольку они используют ресурсы ОС, не связанные с памятью (дескрипторы файлов), которые могут быть исчерпаны независимо от памяти. Вот почему закрытие их явно важно. Но это не относится к ByteArrayOutputStream
, основанному исключительно на памяти.
Метод Translate () выглядит правильно. Используя блокировку, вы запрещаете другим добавлять или иным образом изменять список во время работы в Translate/AddRange.
Я думаю, что может быть проблема с вашей собственностью IsReadyOnly. При внутреннем чтении/записи свойства используется блокировка. Но есть и публичный геттер, который не заперт. Возможно, поток 1 вызывает MarkAsReadOnly, в то время как второй поток может получить значение false при просмотре IsReadOnly. Вместо этого я бы использовал нормальное свойство и либо заблокировал бы его, либо использовал летучее поле.