Новое значение из ThreadLocal<IDisposable> после.Value.Dispose()
Есть ли встроенный ThreadLocal<T>
-подобная конструкция для совместного использования объекта в каждом уникальном потоке, но воссоздания его, если исходное значение было уничтожено / уничтожено / сорвано / обнулено?
Вот моя попытка реализовать такое поведение с ConcurrentDictionary
(ThreadLocalDisposable2
ниже), но я надеялся просто использовать ThreadLocal<T>
(как в ThreadLocalDisposable1
), однако я не могу получить Foo
пройти тест, .Values.Remove(this)
не делает то, что я надеялся, что это будет делать и до сих пор вызывает ObjectDisposedException
,
public class Class1
{
[Test]
public void Foo()
{
using (var foo = ThreadLocalDisposable1.Get())
foo.Foo();
using (var foo = ThreadLocalDisposable1.Get())
foo.Foo();
}
[Test]
public void Bar()
{
using (var bar = ThreadLocalDisposable2.Get())
bar.Foo();
using (var bar = ThreadLocalDisposable2.Get())
bar.Foo();
}
}
[1]
public class ThreadLocalDisposable1 : IDisposable
{
private Stream _foo;
private static ThreadLocal<ThreadLocalDisposable1> _thread;
static ThreadLocalDisposable1()
{
_thread = new ThreadLocal<ThreadLocalDisposable1>(() => new ThreadLocalDisposable1(), true);
}
private ThreadLocalDisposable1()
{
_foo = new MemoryStream();
}
public static ThreadLocalDisposable1 Get()
{
return _thread.Value;
}
public void Foo()
{
_foo.WriteByte(1);
}
public void Dispose()
{
//I do not think it means what I think it means
_thread.Values.Remove(this);
_foo.Dispose();
}
}
[2]
public class ThreadLocalDisposable2 : IDisposable
{
private Stream _foo;
private int _thread;
private static ConcurrentDictionary<int, ThreadLocalDisposable2> _threads;
static ThreadLocalDisposable2()
{
_threads = new ConcurrentDictionary<int, ThreadLocalDisposable2>();
}
private ThreadLocalDisposable2(int thread)
{
_thread = thread;
_foo = new MemoryStream();
}
public static ThreadLocalDisposable2 Get()
{
return _threads.GetOrAdd(Thread.CurrentThread.ManagedThreadId, i => new ThreadLocalDisposable2(i));
}
public void Foo()
{
_foo.WriteByte(1);
}
public void Dispose()
{
ThreadLocalDisposable2 thread;
_threads.TryRemove(_thread, out thread);
_foo.Dispose();
}
}
Редактировать:
Просто чтобы уточнить, что я имею в виду, в основном я хочу, чтобы все поведение ThreadLocal
но когда я звоню Dispose
(по стоимости, ThreadLocalDisposable*
с базовым Stream
в этом примере не статический ThreadLocal
сам) вывести этот ликвидированный экземпляр из обращения, т. е. при повторном вызове - создать новую ценность, как если бы это была совершенно новая нить, требующая нового экземпляра.
ThreadLocalDisposable1
, [1], это пример класса того, что, я думаю, должно было сработать, кроме .Values.Remove(this)
line не "выводит его из обращения" и заставляет создавать новый экземпляр для этого потока.
ThreadLocalDisposable2
, [2], с ConcurrentDictionary
это способ, которым я реализовал альтернативу ThreadLocal с поведением "вынуть из обращения", который я преследую.
Редактировать:
Это не реальный вариант использования, который я имею, просто общий пример, который я могу придумать, но если у вас есть, например, статический ThreadLocal<SqlConnection>
или сокет, и он принудительно закрыт (и расположен в последнем блоке) - отбросьте тот экземпляр подключения и создайте новый прозрачно при повторном вызове.
1 ответ
Кажется, ты делаешь это намного сложнее, чем должно быть. Учти это:
public class MyClass: IDisposable
{
private Stream _foo;
public MyClass Get()
{
if (_foo == null)
{
_foo = new MemoryStream();
}
}
public void Foo()
{
_foo.WriteByte(1);
}
public void Dispose()
{
if (_foo != null)
{
_foo.Dispose();
_foo = null;
}
}
}
Теперь вы можете создать один из них:
ThreadLocal<MyClass> MyThing = new ThreadLocal<MyClass>();
И вы можете написать:
using (MyThing.Value.Get())
{
// do stuff
}
Это кажется функционально эквивалентным тому, что вы пытаетесь сделать со своим ConcurrentDictionary
вещи.
Тем не менее, кажется, что это то, что было бы лучше управлять другим способом. Я не знаю ваше приложение, поэтому не могу сказать наверняка, но кажется плохой идеей иметь объект с состоянием, такой как Stream
или же SqlConnection
как глобальная переменная. Обычно эти вещи зависят от задания, а не от потока, и поэтому должны передаваться в качестве параметров при запуске задания.