Дано: Создание объекта ADO Connection из одного потока и передача его другому потоку - это запрещенный. Два потока - это разные апартаменты, и даже если первый поток никогда снова коснется его (даже не будет ссылаться на него!), Это не имеет значения.
Этот объект ADO Connection был создан ThreadA, ThreadA - единственный поток, когда-либо, при любых обстоятельствах, когда-либо, когда-либо, когда-либо разрешено использовать этот объект Connection.
Теперь замените «ADO Connection» на «ADO.NET Connection». Применяется ли то же правило?
я знаю, что большинство объектов в .NET framework не являются потокобезопасными. Например, структура DictionaryEntry в SDK говорит:
Thread Safety
Any public static members of this type are thread safe. Any instance members are not guaranteed to be thread safe.
Я понимаю, что не потокобезопасный означает, что мне нужно синхронизировать доступ к объекту, если я собираюсь получить к нему доступ из разных потоков. Это все хорошо, и я мог обеспечить одновременный доступ к объекту только одного потока:
lock (myObject)
{
...
}
Но есть нечто большее, чем просто отказ от потоковой передачи.
В COM (некоторые) объекты привязаны к «квартира», который их создал. После того, как объект был построен в одном апартаменте, у вас будет запрещенный для доступа к нему из другого апартамента - независимо от того, насколько вы защищаете этот объект от одновременного доступа нескольких потоков.
Существует ли аналогичная концепция в .NET?
Я знаю, что вам запрещен доступ к элементам управления из потоков, отличных от того, который его создал, даже если вы используете его поточно-ориентированным способом. Это не задокументировано в MSDN:
Thread Safety
Only the following members are thread safe: BeginInvoke, EndInvoke, Invoke, InvokeRequired, and CreateGraphics if the handle for the control has already been created. Calling CreateGraphics before the control's handle has been created on a background thread can cause illegal cross thread calls.
Нет упоминания о том, что элементы управления генерируют исключения, когда вы создаете и используете их из одного потока - когда этот поток не является первым потоком, который был создан при запуске приложения.
А как насчет произвольных объектов? Что о:
public class MyClass
{
int _number;
public int Number { get { return _number; } set { _number = value; } }
}
MyClass myObject = new MyClass();
Пока я синхронизирую доступ к myObject, двум потокам разрешено разговаривать с ним?
То же самое касается:
List<Object> sharedList = new List<Object>();
Два потока могут общаться со списком, если они не делают это одновременно, обычно с помощью:
lock (sharedList)
{
sharedList.Add(data);
}
разрешено ли двум потокам касаться одного и того же объекта?
То же самое касается:
IAsyncResult ar = BeginSetLabelToTheValueINeed(label1);
...
EndSetLabelToTheValueINeed(ar);
То же самое касается:
//Fetch image on connection that is an existing DB transaction
public static Bitmap GetImageThumbnail(DbConnection conn, int imageID)
{
}
преобразуется в шаблон асинхронного делегата:
//Begin fetching an image on connection that is an existing DB transaction
IAsyncResult ar = BeginGetImageThumbnuts(conn, imageID, callback, stateOjbect);
...
//Finish fetching an image on connection that is an existing DB transaction
Bitmap thumb = EndGetImageNumbthail(ar);
Вместо того чтобы отвечать на вопрос, люди начали обсуждение шаблонов проектирования в ADO.NET. Пожалуйста, ответьте на вопрос. Не обращайте внимания на примеры, если они сбивают с толку и отвлекают ваш беличий мозг.
мои беличьи мозги в банке, занятые генерацией случайных чисел для АНБ, поэтому они не могут ответить на ваш вопрос.
Видимо, да. А обида ???





Зачем вам создавать соединение в одном потоке и использовать его из другого?
Время между созданием / открытием и закрытием / удалением соединения должно быть минимальным, насколько это возможно, поэтому передача его другому потоку не кажется хорошей идеей, независимо от того, возможно это или нет.
Если тебе это не нравится, кусай меня.
Во-первых, вы не требуется для синхронизации доступа к объектам в многопоточных сценариях. Вы можете свободно «возиться» с любыми членами ваших собственных классов из нескольких потоков без каких-либо ограничений со стороны CLR. Тот факт, что форма (и элементы управления в целом) не позволяют вам вызывать участников в другом потоке, является деталью реализации (и той, которая не была явной до .Net 2.0).
При этом вы обычно используете необходимость, чтобы обеспечить какой-либо тип синхронизации для выполнения чего-либо полезного с объектами, доступными для нескольких потоков.
К вашему конкретному вопросу. Я считаю, что SqlConnection можно использовать из нескольких потоков, но не одновременно. Невозможность одновременного использования SqlConnection почти полностью сводит на нет любое преимущество в производительности, которое вы можете получить при использовании общего соединения. Учитывая, насколько дешево создание нового SqlConnection (особенно после объединения в пул), повторное использование такого соединения не имеет смысла.
Я знаю лишь несколько случаев, когда я желаю того же:
Помимо этих двух (плюс, может быть, еще нескольких?), Вы действительно не хотите делать то, о чем просите здесь.
Я согласен с StingyJack, в большинстве случаев ваши объекты ADO должны появляться в пределах использования (создаваться и уничтожаться в пределах области видимости) и быть локальными переменными, тем самым устраняя проблемы с потоками.
Сделка с WinForm заключается в разнице между сообщениями POST и SEND (отправка сообщений блокируется потребителю до их завершения). На самом деле это не вызывает исключения, исключением является просто предупреждение, добавленное в 2.00 при отладке. На практике, без исключения, это приводит к (кажущимся) случайным блокировкам.
См. Эту удобную страницу от Алика Левина
.net объект моделируется MTA. и это только предположение, если вы взаимодействуете с COM-объектами
Pitfalls With .Net Multithreading And COM Objects – Threads Must Have Compatible Apartment Models (MTA vs. STA)
Be alert when implementing multithreading .Net in conjunction with COM objects. Thread apartment models matter.
.Net threads have Multi Threaded Apartment (MTA) model by default. COM objects have Single Thread Apartment (STA). Calling on COM objects on .Net threads that you spawn may cause unpredicted result.
Multithreading in .Net is easily implemented based on either Thread or ThreadPool objects. Thread.Start() method spawns new thread which has Multi Threaded Apartment (MTA) model . ThreadPool.QueueUserWorkItem(myMethod) queues myMethod to be executed on available thread managed by it.
Объекты в .NET не привязаны к квартирам, если вы специально не сделаете их так.
Например, если вы используете локальное хранилище потока, у вас будут проблемы с несколькими потоками, обращающимися к объекту, если вы предполагаете, что весь доступ к объекту будет происходить в одном потоке.
С другой стороны, локальное хранилище потока может быть функцией, в которой использование объекта в нескольких потоках обрабатывается, по крайней мере, частично отдельно.
Что касается других ваших случаев, давайте рассмотрим их по очереди с комментариями:
А как насчет произвольных объектов? Что о:
public class MyClass
{
int _number;
public int Number { get { return _number; } set { _number = value; } }
}
MyClass myObject = new MyClass();
Пока я синхронизирую доступ к myObject, двум потокам разрешено разговаривать с ним?
Отвечать: Да, и в этом случае необходимость синхронизации зависит от ваших требований.
Если с точки зрения времени вы абсолютно должны гарантировать, что если в одном потоке установите новое значение в объект, а в другом прочитаете его сразу после его установки (но два разных потока), вам потребуется, чтобы второй поток считал значение первого вставьте, тогда вам понадобится блокировка.
Однако сохранение значения int - это атомарная операция. Блокировка необходима для создания барьера памяти, чтобы при чтении значения во втором потоке не использовалась кэшированная копия.
Обычно вы можете обойтись такими простыми классами, просто объявляя вещи изменчивыми, но блокировка - верный способ получить лучшие гарантии.
Для более сложного объекта, скажем, установка структуры больше, чем собственный размер шины процессора (то есть больше, чем 32 или 64-битный, в зависимости от), требуется блокировка, поскольку копирование данных в нужное место в памяти не является Это не атомарная операция в таком случае. Другими словами, без блокировки вы рискуете прочитать половину старых и половину новых данных, если один поток попытается прочитать данные в середине операции записи, выполняемой в другом потоке.
То же самое касается:
List<Object> sharedList = new List<Object>();
Два потока могут общаться со списком, если они не делают это одновременно, обычно с помощью:
lock (sharedList)
{
sharedList.Add(data);
}
разрешено ли двум потокам касаться одного и того же объекта?
Отвечать: Да. Обычно рекомендуется использовать свойство SyncRoot интерфейса ICollection для коллекций или просто использовать другой объект блокировки вообще.
То же самое касается:
IAsyncResult ar = BeginSetLabelToTheValueINeed(label1);
...
EndSetLabelToTheValueINeed(ar);
Отвечать: Я не уверен, какая именно здесь проблемная переменная? Если label1, то здесь нет ничего, что препятствовало бы доступу нескольких потоков и вмешательству с этой переменной, и вам необходимо использовать механизмы блокировки для предотвращения таких проблем.
То же самое касается:
//Fetch image on connection that is an existing DB transaction
public static Bitmap GetImageThumbnail(DbConnection conn, int imageID)
{
}
преобразуется в шаблон асинхронного делегата:
//Begin fetching an image on connection that is an existing DB transaction
IAsyncResult ar = BeginGetImageThumbnuts(conn, imageID, callback, stateOjbect);
...
//Finish fetching an image on connection that is an existing DB transaction
Bitmap thumb = EndGetImageNumbthail(ar);
Отвечать: если под этим вы имеете в виду, что вы будете выполнять несколько таких запросов параллельно, вы, скорее всего, не сильно выиграете, так как вам нужно будет сериализовать доступ к объекту подключения, поскольку они не являются потокобезопасными. Вам, вероятно, лучше сделать метод эскизов открытым и закрыть собственное соединение.
Если, конечно, вы не хотите использовать только одно соединение и вместо этого сериализовать этот доступ, но распараллелить вычисление эскиза, но поскольку эти потоки каким-то образом будут выполняться последовательно, из-за того, что большинство из них ждут, пока первый закончит доступ к связи, вы, скорее всего, не выиграете здесь много.
Короче говоря, блокировку обычно очень легко реализовать и почти всегда можно гарантировать, что вы не сможете испортить объект, обратившись к нему из двух потоков.
Вы не можете разговаривать с элементом управления из нескольких потоков - вообще - независимо от того, сколько вы выполняете синхронизации. Даже если вы касаетесь его только из фонового потока, а не из потока графического интерфейса, это запрещено.
Это зависит от контроля. Если это встроенный элемент управления, конечно, но вы делаете его сами, то да, вы можете нанести здесь большой ущерб.
Объекты .NET не привязаны к квартире. Квартиры действительно вступают в игру только тогда, когда ваш .NET-код взаимодействует с COM-объектами, хотя это не сложно сделать бессознательно. Например, если вы взаимодействуете с буфером обмена Windows, вы активируете подсистему COM. Квартиры устанавливаются на уровне потока, и после того, как ваш поток установил свою квартиру (MTA, STA и т. д.), Она не может быть изменена, поэтому очень важно получить правильную квартиру. Все приложения WinForms имеют [STAThread], прикрепленный к их методу Main (), чтобы гарантировать, что основной поток приложения (то есть поток перекачки сообщений GUI) является STA и может взаимодействовать с такими удобными средствами, как буфер обмена Windows.
Среда выполнения .NET должна знать о подсистеме COM, чтобы обеспечивать функциональные возможности взаимодействия с COM. Однако внутренние ужасающие детали возникают довольно редко.
Насколько я понимаю, одна из целей разделения COM-объектов на разные квартиры заключалась в том, чтобы приложения могли использовать программные компоненты, разработанные с использованием различных потоковых моделей, не беспокоясь о таких проблемах.
Здесь возникает множество сложных проблем, в частности, идея о том, что однопоточные библиотеки часто не построены для работы с повторным входом и, конечно же, не поддерживают идею одновременного изменения объекта двумя потоками выполнения.
Например, предположим, что вы хотите использовать COM-объект в своем новом многопоточном приложении C++. Этот COM-объект был разработан в Visual Basic и не является потокобезопасным, но вы хотите использовать несколько экземпляров этого компонента из нескольких потоков. Хотя верно, что вы можете добавить какой-то механизм блокировки для предотвращения неправильного доступа, тот факт, что объект существует в своей собственной однопоточной квартире (STA), означает, что подсистема COM будет сериализовать доступ к объекту от вашего имени. Вам не обязательно знать потоковую модель COM-объекта, чтобы использовать его, или, по крайней мере, это обычно работает.
.NET был написан для нашего нового многопоточного мира и поэтому не использует концепции квартир. Среда выполнения определенно знает о них и должна поддерживать взаимодействие с устаревшими COM-объектами.
Квартиры очень сложные, и многие странные вещи могут пойти не так, если вы не будете знать, что делаете. Радуйтесь, что вам обычно не нужно беспокоиться об этом в .NET.
Если вам нужна действительно серьезная основная информация, вы можете проверить Квартиры и насос в ЦЛР.
спасибо, что указали на мои ошибки. Я обновил свой ответ; к сожалению, однажды полученную награду у вас не отнять. Я буду иметь в виду, когда буду читать ответы, хотя подмигиваю.
Это не совсем так. Вам не разрешено прикасаться к свойствам визуальных элементов управления из потока, отличного от того, который его создал. Этот элемент управления «привязан» к потоку, который его создал. Дело не в синхронизированном доступе: это просто запрещено.
Верно, но я понял ваш вопрос в более общем смысле. Да, некоторые ресурсы уровня ОС или библиотеки, которые используются объектами .NET, привязаны к потоку.
СТРЕЛЬБА за щедрость!
Хорошо, некоторые классы / структуры классов в .NET имеют методы, привязанные к квартире, но ТОЛЬКО ПО ДИЗАЙНУ. Это означает, что для этого вам нужно УКАЗАТЬ СПЕЦИАЛЬНЫЙ КОД. По умолчанию он не установлен. Кодирование для этого довольно неуклюже. Вы должны получить идентификатор потока, который хотите придерживаться, и постоянно его проверять. Я не думаю, что во фреймворке есть какие-либо средства, упрощающие эту задачу, кроме средств для компонентов пользовательского интерфейса, отслеживающих поток пользовательского интерфейса. Эта структура поддержки, вероятно, спрятана глубоко в стеке пользовательского интерфейса; Я никогда не видел этого ни в каких документах MSDN.
Что более распространено, так это установка квартиры потока. Многие части всей структуры ограничены потоками STA по дизайну (STAThreadAttribute). Обычно это происходит потому, что они в глубине своего ядра касаются COM-объектов, которые часто требуют, чтобы они использовались только в потоках STA. Например, основной поток в приложениях winforms отмечен атрибутом STAThreadAttribute, поэтому каждый компонент пользовательского интерфейса может быть затронут только в том же потоке, который они создали, - потоке пользовательского интерфейса. Интересно, что XamlReader в WPF знает о текущем апартаменте потока и даже не выполняет десериализацию компонентов пользовательского интерфейса, если поток, в котором он выполняется, не является потоком STA.
«Имеет ли стандартный POCO сходство потоков или заботится о том, находится ли он в потоке STA или MTA?» Ответ - нет, если только вы не запрограммируете это так или не пометите его методы атрибутом STAThreadAttribute или если объект создается в стеке вызовов, который где-то в своей иерархии имеет метод, помеченный атрибутом STAThreadAttribute.
«Имеет ли объект ADO.NET привязку к потоку и знает ли он о квартире?»
Ну, DbConnection расширяет Component, который расширяет MarshalByRefObject. Component, MBRO, DbConnection и его потомки (то есть SqlConnection) не содержат методов, отмеченных атрибутом STAThreadAttribute. Быстрая проверка их основных зависимостей (таких как DbTransaction) не обнаруживает, что какой-либо из методов ЭТИХ классов также помечен только как STA. Поэтому они не осведомлены о квартире.
Быстрый просмотр с помощью Reflector не обнаруживает очевидного наблюдения за идентификатором потока. DbConnection действительно зависит от Transaction, который кажется наиболее вероятным любителем потоков из всей группы.
Я вытащил Reflector и проверил несколько из этих классов.
DbConnection - не заботится о ID
текущего потока
SqlConnection - отслеживает некоторые потоки при отладке.
SqlTransaction - имеет функцию под названием «ZombieCheck», но не просматривает потоки.
DbTransaction - здесь ничего.
Поэтому я думаю, что можно с уверенностью сказать, что объекты ADO.NET НЕ имеют сходства потоков. Я даже не видел, чтобы они использовали Thread.Begin / EndThreadAffinity.
РЕДАКТИРОВАТЬ
Как указано в комментариях Джереми, я продолжал говорить о КЛАССАХ, отмеченных атрибутом, а не о МЕТОДАХ КЛАССОВ. ОГРОМНАЯ ошибка. Я облажался, создав впечатление, что смотрю только на определения классов, а не на весь класс.
Я отредактировал это, чтобы прояснить, и позвольте мне сказать это здесь, чтобы быть абсолютно ясным: никакие методы класса в System.Data не отмечены атрибутом STAThreadAttribute. Вы можете проверить это сами с помощью следующего хакерского кода:
SqlConnection sc = new SqlConnection();
var data = AppDomain.CurrentDomain.GetAssemblies()
.Where(x => x.FullName.StartsWith("System.Data,")).First();
var dtypes = data.GetTypes();
var results = new List<string>();
foreach (var type in dtypes)
foreach (var method in type.GetMethods())
foreach (var attr in
method.GetCustomAttributes(true).OfType<System.Attribute>())
{
results.Add(string.Format("{0} {1} {2}",
type.Name, method.Name, attr.GetType().Name));
if (attr.GetType() == typeof(STAThreadAttribute))
throw new Exception("SHIII");
}
Кроме того, классы пользовательского интерфейса в winforms также не используют атрибут STAThreadAttribute; это приложение, в котором используется этот атрибут (как, опять же, указал Джереми).
Есть особый случай, когда я столкнулся с классом, знающим квартиру. XamlReader, который десериализует xaml и создает классы пользовательского интерфейса WPF, генерирует исключение, когда его метод Load вызывается в потоке MTA. Его даже нет в документации, что раздражает ... Итак, опять же, классы по умолчанию не знают о квартирах; вам нужно закодировать его таким образом, или вы должны сообщить их экземплярам, добавив их в цепочку методов, содержащую метод, отмеченный атрибутом STAThreadAttribute. Уф. Это сложнее объяснить, чем понять ...
Вы выигрываете, потому что конкретно указали на связь между [STAThread] и объектами, находящимися в квартире. Похоже, это указывает на то, что единственный способ блокирования объектов - это STAThread. Таким образом, объекты не привязаны к квартире без тега [..apartment model ...]. Очень хорошо.
Спасибо. Надеюсь, я использую ваши 300 бонусных баллов, чтобы выплатить пару лично!
Одно небольшое исправление: вы не отмечаете объект с помощью [STAThread], вы отмечаете метод и, в частности, отмечаете точку входа в свою программу (Main). После того, как вы установили квартиру для резьбы, вы не можете ее поменять. Добавление [STAThread] к случайному объекту не имеет никакого эффекта.
Тогда я думаю, мне нужно вспомнить ответ о награде. Если [STAThread] используется для установки потоковой модели приложений, исходный вопрос все еще остается в силе: привязаны ли объекты к апартаментам?
Награда на самом деле не имеет значения, я обновил свой ответ, чтобы отразить, как (я считаю) все работает. COM - это сложно! :)
Ух, я облажался. Я уточнил и добавил кое-что, чтобы это исправить. Очевидно, что для подобных случаев система вознаграждений должна позволять вернуть вознаграждение в течение X дней. По крайней мере, проголосовал за Джереми.
:) Как я уже сказал, не имеет значения. Ваш ответ очень информативен, просто моя программистская чрезмерная точность не могла отпустить его!
Моя программистская чрезмерная точность бьет меня головой из-за того, что я не уловил этого в три раза, когда перечитывал его до, во время и после публикации.
Так что после всего этого я все еще не понимаю. Я создаю объект ListView в одном потоке, попытка коснуться его из другого потока вызывает исключение. Не привязан ли ListView к создавшему его потоку?
Базовые элементы управления Windows привязаны к потоку, но не к объекту C#, который его представляет. Или, в более общем смысле, иногда объект C# управляет базовым ресурсом, привязанным к потоку. Если это так, я думаю, вы могли бы сказать, что он «наследует» это свойство. Но случайные объекты C# не привязаны.
Опять же, привязка квартиры НЕ выполняется автоматически. Для этого вам нужно КОДИРОВАТЬ. Классы пользовательского интерфейса ЗАПИСАНЫ для этого. Всем остальным классам все равно.
Работает ли баунти над вики-вопросами?