Многопоточность на многоядерных машинах без максимальной загрузки процессора

2

Я работаю над тем, чтобы поддерживать код другого пользователя, который использует многопоточность, двумя способами:

1: ThreadPool.QueueUserWorkItem(New WaitCallback(AddressOf ReadData), objUpdateItem)

2: Dim aThread As New Thread(AddressOf LoadCache)
   aThread.Start()

Однако на двухъядерной машине я получаю только 50% -ную загрузку процессора, а на двухъядерном процессоре с поддержкой hyperthreadin я получаю только 25% загрузки процессора.

Очевидно, что потоки чрезвычайно сложны, но это поведение, по-видимому, указывает на то, что я не понимаю какой-то простой фундаментальный факт?

UPDATE

Код слишком ужасно сложный для публикации здесь, к сожалению, но для справочных целей здесь примерно что происходит.... У меня около 500 учетных записей, данные которых загружаются из базы данных в кеш памяти... каждая учетная запись загружается отдельно, и этот процесс сначала вызывает длительную хранимую процедуру, за которой следуют манипуляции и кэширование возвращаемых данных. Таким образом, точка потоки в этой ситуации состоит в том, что на самом деле есть узкое место, попавшее в базу данных (то есть: поток будет работать на холостом ходу в течение 30 секунд, ожидая возвращения запроса), поэтому мы потоки, чтобы другие могли начать обработку данные, полученные из Oracle.

Итак, основной поток выполняет:

ThreadPool.QueueUserWorkItem(New WaitCallback(AddressOf ReadData), objUpdateItem) 

Затем, ReadData() затем переходит к выполнению (ровно один раз):

Dim aThread As New Thread(AddressOf LoadCache)
aThread.Start()

И это происходит в рекурсивной функции, поэтому QueueUserWorkItem может выполняться несколько раз, что, в свою очередь, выполняет ровно один новый поток через aThread.Start

Надеюсь, это даст достойное представление о том, как все происходит.

Итак, по этому сценарию, если это теоретически не выровнять оба ядра, а не максимизировать на 100% на одном ядре, в то время как другое ядро ​​по существу не работает?

  • 1
    Мы не можем ответить, не зная, что делают методы ReadData и / или LoadCache.
  • 1
    Убедитесь, что ReadData и LoadCache используют процессор достаточно долго для измерения. Если какой-либо метод ожидает некоторого ресурса (например, файла в файловой системе, или WaitHandle, или блокировки), он не будет использовать какой-либо процессор, поскольку поток находится в режиме ожидания.
Показать ещё 1 комментарий
Теги:
multithreading

6 ответов

4
Лучший ответ

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

@Judah Himango - Я предположил, что эти две строки кода были образцами того, как многопоточность достигалась в двух разных местах программы. Возможно, ОП может уточнить, если это так, или если эти две строки действительно находятся в одном методе. Если они являются частью одного метода, нам нужно будет увидеть, что на самом деле делают эти два метода.

Обновление:
Это звучит так, как будто это должно максимизировать оба ядра. Что вы подразумеваете под рекурсивным вызовом ReadData()? Если каждый новый поток только вызывает ReadData в конце или рядом с ним, чтобы начать следующий поток, это может объяснить поведение, которое вы видите.
Я не уверен, что это действительно хорошая идея. Если хранимая процедура занимает 30 секунд, чтобы получить данные, то, предположительно, она размещает справедливую нагрузку на сервере базы данных. Запуск его 500 раз параллельно - это еще хуже. Очевидно, что я не знаю вашу базу данных или данные, но я бы посмотрел на улучшение производительности хранимой процедуры.
Если многопоточность выглядит как путь вперед, тогда у меня будет цикл в основном потоке, который вызывает ThreadPool.QueueUserWorkItem один раз для каждой учетной записи, которая требует загрузки. Я бы также удалил явное создание потоков и использовал только пул потоков. Таким образом, вы менее склонны голодать на локальном компьютере, создавая слишком много потоков.

  • 0
    Код запускает 2 потока, верно? Один поток пула, другой созданный пользователем поток.
  • 0
    Я добавил дополнительную информацию, которая, надеюсь, иллюстрирует происходящее.
Показать ещё 1 комментарий
3

Сколько потоков вы вращаетесь? Это может показаться примитивным (подождите несколько лет, и вам больше не понадобится это делать), но ваш код должен найти оптимальное количество потоков, чтобы начать, и открутить их. Простое выполнение одного потока не ускорит работу и не привяжет физический процессор, хотя это может быть полезно и по другим причинам (рабочий поток должен поддерживать ваш пользовательский интерфейс, например).

Во многих случаях вам нужно будет запускать несколько потоков, равных количеству доступных логических ядер (доступный из среды Environment.ProcessorCount, я считаю), но он может иметь некоторые другие основы. Я объединил несколько десятков потоков, разговаривая с разными хостами, например, когда я связан с задержкой удаленного процесса.

  • 0
    По словам Джо Даффи, ваш код никогда не должен определять оптимальное количество потоков для запуска. Вместо этого используйте ThreadPool, который сделает лучший выбор для вас на машине, на которой вы работаете. Environment.ProcessorCount выглядит как правильный инструмент, но он читается прямо из среды, в которую можно записывать и читать.
  • 0
    У вас есть ссылка, плинтус?
Показать ещё 4 комментария
2

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

Что вы ищете, это Parallelism. Рамка .NET 4.0 добавит множество новых функций для поддержки Parallelism. Здесь есть пик-пик:
http://www.danielmoth.com/Blog/2009/01/parallelising-loops-in-net-4.html

0

Сколько у вас потоков, и у вас есть блокировки в LoadCache. SyncLock может использовать многопоточную систему как отдельный поток (по дизайну). Кроме того, если в вашем единственном потоке только один поток, вы получите только один рабочий поток.

0

Использование ЦП предполагает, что вы используете только одно ядро; это может означать, что вы добавили потоковую передачу в часть, где это не выгодно (в этом случае, когда время процессора не является горлышком бутылки).

Если загрузка кеша или данных чтения происходит очень быстро, многопоточность не обеспечит существенного улучшения скорости. Аналогично, если вы столкнулись с другим узким местом (медленная пропускная способность для сервера и т.д.), Он может не отображаться как использование ЦП.

0

Поведение процессора указывает, что приложение использует только один логический процессор. 50% будет одним из 2 из proc (proc + proc). 25% - один логический процессор из 4 (proc + HT + proc + HT)

Ещё вопросы

Сообщество Overcoder
Наверх
Меню