Что на самом деле означает «потокобезопасный» ... На практике - PullRequest
40 голосов
/ 09 марта 2010

пожалуйста, ответьте на мои вопросы новичка ..

Я пытался конвертировать PDF в PNG, используя ghostscript, с ASP.NET и C #. Тем не менее, я также прочитал, что ghostscript не является потокобезопасным. Итак, мои вопросы:

  1. Что именно означает "ghostscript не является потокобезопасным" на практике? Как это повлияет, если я использую его в живом веб-приложении ASP.NET (aspx), когда к нему одновременно обращаются одновременно многие пользователи?

  2. Я также читал с другого сайта, что главная особенность ghostscript ver. 8.63 - многопоточный рендеринг. Означает ли это, что наш потокобезопасный вопрос теперь решен? Безопасен ли поток ghostscript?

  3. Я также оцениваю PDF2Image из PDFTron, который должен быть поточно-ориентированным. Но лицензия на процессор не из дешевых. Стоит ли платить дополнительные деньги за «потокобезопасность» против «небезопасно»?

Ответы [ 7 ]

34 голосов
/ 10 марта 2010

Трудно придумать точное техническое определение, с которым все согласны.

Неформально «потокобезопасный» просто означает «достаточно хорошо ведет себя при вызове из нескольких потоков». Объект не будет падать или давать сумасшедшие результаты при вызове из нескольких потоков.

Вопрос, на который вам действительно нужно получить ответ, если вы намереваетесь выполнять многопоточное программирование с использованием определенного объекта, заключается в следующем: «Какую модель потоков ожидает объект?»

Существует множество различных моделей потоков. Например, модель с «свободной резьбой» гласит: «делай все, что хочешь, от любого потока; объект с этим справится». Это самая простая модель для вас и самая трудная для поставщика объектов.

На другом конце спектра находится «однопотоковая» модель - все экземпляры всех объектов должны быть доступны из одного потока, точка.

А потом в центре куча вещей. Модель «многоуровневая» - это «вы можете создать два экземпляра в двух разных потоках, но независимо от того, какой поток вы используете для создания экземпляра, это поток, который вы всегда должны использовать для вызова методов в этом экземпляре».

Модель "аренды с резьбой" гласит: "Вы можете вызывать один экземпляр в двух разных потоках, но вы несете ответственность за то, чтобы ни один из потоков не делал это одновременно".

И так далее. Узнайте, какую модель потоков ожидает ваш объект, прежде чем пытаться написать код потока для него.

22 голосов
/ 10 марта 2010

Учитывая, что коллекция, например, не является безопасной:

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, другой может попробовать GetGetValue (). Поскольку коллекции не могут быть прочитаны и записаны одновременно, возникнет исключение.

Однако, с другой стороны, если вы попробуете это:

// 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 () только там, где это удобно, или если вы не используете многопоточность самостоятельно, определенно не стоит платить за безопасность потоков. Это означает только то, что если ВАШЕ приложение использует многопоточность, то вы не пострадаете от последствий того, что библиотека не будет поточно-ориентированной. Если вы не пользуетесь многопоточностью, платить за многопотоковую библиотеку не стоит.

10 голосов
/ 26 июня 2011

Я разработчик Ghostscript и не буду повторять общую теорию о безопасности потоков.
Мы работаем над тем, чтобы GS стал потокобезопасным, чтобы можно было создавать несколько «экземпляров» с помощью gsapi_new_instance из один процесс, но мы еще не выполнили это к нашему удовлетворению (которое включает в себя наше тестирование QA).
Тем не менее, графическая библиотека является поточно-ориентированной, и многопоточный рендеринг использует это, чтобы позволить нам порождать несколько потоков для рендеринга полос из списка отображения параллельно. Многопоточный рендеринг подвергся значительному тестированию качества и используется многими коммерческими лицензиатами для повышения производительности многоядерных процессоров.

Вы можете поспорить, что мы объявим, когда наконец поддержим несколько экземпляров GS. Большинство людей, которые хотят использовать текущий GS из приложений, которым требуется несколько экземпляров, порождают отдельные процессы для каждого экземпляра, так что GS не должен быть потокобезопасным. GS может выполнить задание, как определено параметрами списка аргументов, или ввод / вывод может быть передан в / из процесса для предоставления данных и сбора выходных данных.

10 голосов
/ 09 марта 2010

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 безопасным, убедитесь, что к нему одновременно обращается только один поток. Вы можете сделать это через замки:

lock(someObject)
{
   thread1.Use(someGSObject);
}
lock(someObject)
{
   thread2.Use(someGSObject);
}
3 голосов
/ 09 марта 2010

Если вы используете ghostscript из объекта оболочки (то есть запускаете командную строку для обработки файла), вы не будете сталкиваться с проблемами потоков, потому что каждый запущенный экземпляр будет работать в отдельном процессе на сервере. Вам нужно быть осторожным, когда у вас есть dll, который вы используете из C # для обработки PDF, этот код должен быть синхронизирован, чтобы не допустить одновременного выполнения одного и того же кода двумя потоками.

2 голосов
/ 09 марта 2010
  1. Потокобезопасный в основном означает, что фрагмент кода будет работать правильно, даже если к нему обращаются несколько потоков. Многочисленные проблемы могут возникнуть, если вы используете непоточный безопасный код в многопоточном приложении. Наиболее распространенная проблема - блокировка. Однако есть гораздо более гнусные проблемы (условия состязания), которые могут быть большей проблемой, потому что проблемы с потоками общеизвестно трудны для отладки.

  2. Нет. Многопоточный рендеринг просто означает, что GS сможет рендериться быстрее, потому что он использует потоки для рендеринга (теоретически, во всяком случае - не всегда так на практике).

  3. Это действительно зависит от того, для чего вы хотите использовать свой рендерер. Если вы собираетесь обращаться к своему приложению с помощью нескольких потоков, то да, вам нужно беспокоиться о том, что оно безопасно для потоков. В противном случае, это не имеет большого значения.

1 голос
/ 09 марта 2010

Вообще это двусмысленный термин.

Thread-Safety может быть на концептуальном уровне, где у вас есть правильная синхронизация ваших общих данных. Обычно это подразумевают авторы библиотек.

Иногда это означает, что параллелизм определяется на уровне языка. модель памяти языка поддерживает параллелизм. Это сложно! потому что, как автор библиотеки, вы не можете создавать параллельные библиотеки, потому что у языка нет гарантий для многих основных примитивов, которые необходимо использовать. Это касается авторов компиляторов больше, чем пользователей библиотек. В этом смысле C # является потокобезопасным.

Я знаю, что не ответил на ваш вопрос напрямую, но надеюсь, что это поможет.

...