Что на самом деле означает "потокобезопасный"... На практике
Пожалуйста, терпите мои вопросы новичка..
Я пытался конвертировать PDF в PNG, используя ghostscript, с ASP.NET и C#. Тем не менее, я также прочитал, что ghostscript не является потокобезопасным. Итак, мои вопросы:
Что именно означает "ghostscript не является потокобезопасным" на практике? Как это повлияет, если я использую его в живом веб-приложении ASP.NET(aspx), когда к нему одновременно обращаются многие пользователи?
Я также читал с другого сайта, что главная особенность ghostscript ver. 8.63 - многопоточный рендеринг. Означает ли это, что наш потокобезопасный вопрос теперь решен? Теперь ghostscript безопасен?
Я также оцениваю PDF2Image из PDFTron, который должен быть потокобезопасным. Но лицензия на процессор не из дешевых. Стоит ли платить дополнительные деньги за "потокобезопасность" против "небезопасно"?
7 ответов
Учитывая, что коллекция, например, не является опасной:
var myDic = new Dictionary<string, string>();
В многопоточной среде это вызовет:
string s = null;
if (!myDic.TryGetValue("keyName", out s)) {
s = new string('#', 10);
myDic.Add("keyName", s);
}
Поскольку один поток работает, пытаясь добавить KeyValuePair в словарь myDic, другой может попробовать TryGetValue(). Поскольку коллекции не могут быть прочитаны и записаны одновременно, возникнет исключение.
Однако, с другой стороны, если вы попробуете это:
// Other threads will wait here until the variable myDic gets unlocked from the preceding thread that has locked it.
lock (myDic) {
string s = null;
if (!myDic.TryGetValue("keyName", out s)) {
s = new string('#', 10);
myDic.Add("keyName", s);
}
} // The first thread that locked the myDic variable will now release the lock so that other threads will be able to work with the variable.
Затем внезапно второй поток, пытающийся получить то же значение ключа "keyName", не должен будет добавлять его в словарь, поскольку первый поток уже добавил его.
Короче говоря, потокобезопасность означает, что объект поддерживает одновременное использование несколькими потоками или будет блокировать потоки соответствующим образом, без необходимости беспокоиться о безопасности потоков.
2. Я не думаю, что GhostScript теперь безопасен для потоков. Он в основном использует несколько потоков для выполнения своих задач, так что это повышает производительность, вот и все.
3. В зависимости от вашего бюджета и ваших требований, он может быть достойным. Но если вы создаете оболочку, возможно, вы можете использовать lock() только там, где это удобно, или если вы не используете многопоточность самостоятельно, определенно не стоит платить за безопасность потоков. Это означает только то, что если ВАШЕ приложение использует многопоточность, то вы не пострадаете от последствий того, что библиотека не будет поточно-ориентированной. Если вы действительно не многопоточны, не стоит платить за многопотоковую библиотеку.
Трудно придумать точное техническое определение, с которым все согласны.
Неформально "потокобезопасный" просто означает "достаточно хорошо ведет себя при вызове из нескольких потоков". Объект не будет падать или давать сумасшедшие результаты при вызове из нескольких потоков.
Вопрос, на который вам действительно нужно получить ответ, если вы собираетесь выполнять многопоточное программирование с использованием определенного объекта, заключается в следующем: "Какую модель потоков ожидает объект?"
Существует множество различных моделей потоков. Например, модель с "свободной резьбой" гласит: "делай все, что хочешь, от любого потока; объект с этим справится". Это самая простая модель для вас, и самая трудная для поставщика объектов.
На другом конце спектра находится "однопотоковая" модель - все экземпляры всех объектов должны быть доступны из одного потока, точка.
И затем есть куча вещей в середине. Модель "квартирный поток" - это "вы можете создать два экземпляра в двух разных потоках, но независимо от того, какой поток вы используете для создания экземпляра, это поток, который вы всегда должны использовать для вызова методов в этом экземпляре".
Модель "аренды с резьбой" гласит: "Вы можете вызывать один экземпляр в двух разных потоках, но вы несете ответственность за то, чтобы ни один из потоков не делал это одновременно".
И так далее. Узнайте, какую модель потоков ожидает ваш объект, прежде чем пытаться написать код потока для него.
Я разработчик Ghostscript и не буду повторять общую теорию о безопасности потоков.
Мы работали над тем, чтобы GS была поточно-ориентированной, чтобы можно было создать несколько "экземпляров" с помощью gsapi_new_instance из одного процесса, но мы еще не выполнили это к нашему удовлетворению (которое включает в себя наше тестирование QA).
Однако графическая библиотека является поточно-ориентированной, и многопоточный рендеринг опирается на это, что позволяет нам порождать несколько потоков для параллельного рендеринга полос из списка отображения. Многопоточный рендеринг подвергся значительному тестированию качества и используется многими коммерческими лицензиатами для повышения производительности многоядерных процессоров.
Вы можете поспорить, что мы объявим, когда наконец поддержим несколько экземпляров GS. Большинство людей, которые хотят использовать текущий GS из приложений, которым требуется несколько экземпляров, порождают отдельные процессы для каждого экземпляра, так что GS не должен быть потокобезопасным. GS может выполнить задание, как определено параметрами списка аргументов, или ввод / вывод может быть передан в / из процесса для предоставления данных и сбора выходных данных.
1) Это означает, что если вы совместно используете одни и те же объекты или поля Ghostscript среди нескольких потоков, это приведет к сбою. Например:
private GhostScript someGSObject = new GhostScript();
...
// Uh oh, 2 threads using shared memory. This can crash!
thread1.Use(someGSObject);
thread2.Use(someGSObject);
2) Я так не думаю - многопоточный рендеринг предполагает, что GS внутренне использует несколько потоков для рендеринга. Это не решает проблему небезопасного использования GS из нескольких потоков.
3) Есть ли там вопрос?
Чтобы сделать поток GhostScript безопасным, убедитесь, что к нему одновременно обращается только 1 поток. Вы можете сделать это с помощью замков:
lock(someObject)
{
thread1.Use(someGSObject);
}
lock(someObject)
{
thread2.Use(someGSObject);
}
Если вы используете ghostscript из объекта оболочки (то есть запускаете командную строку для обработки файла), вы не будете сталкиваться с проблемами многопоточности, потому что каждый запущенный экземпляр будет работать в отдельном процессе на сервере. Вам нужно быть осторожным, когда у вас есть dll, который вы используете из C# для обработки PDF, этот код должен быть синхронизирован, чтобы не допустить одновременного выполнения одного и того же кода двумя потоками.
Потокобезопасность в основном означает, что фрагмент кода будет работать правильно даже при доступе к нескольким потокам. Многочисленные проблемы могут возникнуть, если вы используете не поточно-безопасный код в многопоточном приложении. Наиболее распространенная проблема - блокировка. Однако есть гораздо более гнусные проблемы (условия состязания), которые могут быть большей проблемой, потому что проблемы с потоками общеизвестно трудны для отладки.
Нет. Многопоточный рендеринг просто означает, что GS сможет рендериться быстрее, потому что он использует потоки для рендеринга (теоретически, во всяком случае - не всегда так на практике).
Это действительно зависит от того, для чего вы хотите использовать свой рендерер. Если вы собираетесь получать доступ к своему приложению из нескольких потоков, то да, вам нужно будет позаботиться о том, чтобы оно было поточно-ориентированным. В противном случае, это не имеет большого значения.
Вообще это двусмысленный термин.
Thread-Safety может быть на концептуальном уровне, где у вас есть правильная синхронизация ваших общих данных. Обычно это то, что подразумевают авторы библиотеки.
Иногда это означает, что параллелизм определяется на уровне языка. т.е. модель памяти языка поддерживает параллелизм. Это сложно! потому что, как автор библиотеки, вы не можете создавать параллельные библиотеки, потому что у языка нет гарантий для многих основных примитивов, которые необходимо использовать. Это касается авторов компиляторов больше, чем пользователей библиотек. C# является потокобезопасным в этом смысле.
Я знаю, что не ответил на ваш вопрос напрямую, но надеюсь, что это поможет.