Не подходите близко ни к DataSet, ни к DataAdapter!
Для сброса данных используйте DataReader - используйте текст SQL или вызов Stored Proc через SqlCommand, вызвав для него ExecuteReader. Затем вы можете извлекать записи из DataReader по одной, БЕЗ любого багажа для отслеживания объектов, который поставляется с DateSet, Entity Framework или Linq to SQL, или NHibenate - все эти платформы имеют добавленные слои, чтобы вы могли делать отслеживание объектов и изменений - которое вам не нужно и будет только накладными расходами для вас.
Когда вы записываете свои результаты обратно в базу данных, делайте это с помощью SqlBulkCopy, с включенной функцией TableLock и со свойствами базы данных, для которых для «Модель восстановления» установлено любое значение, кроме «Полный». Убедитесь, что ограничения для целевой таблицы отключены, и что индексы не определены (затем удалите и заново создайте в конце, если вам нужно).
SqlBulkCopy выполняет свою собственную пакетную обработку при отправке обратно на SQL Server, ЕСЛИ вы обязательно указываете BatchSize (по умолчанию все в одной партии). Возможно, вы также захотите установить UseInternalTransaction для SqlBulkCopy, чтобы каждый пакет выполнялся в отдельной транзакции - это еще больше сократит использование журнала транзакций.
Читатель и ветка писателя могут помочь, а могут и нет, я не профилировал разницу. Вам также может понадобиться один или несколько потоков обработки или другой механизм, если упомянутый вами «сторонний процесс» отнимает много времени.
Можно сделать все это в одном потоке, по одной записи за раз, и это может быть довольно быстро (в зависимости от стоимости обработки, которую вы выполняете).
Если вам НУЖНО использовать несколько потоков, НЕ ОБМЕНЯЙТЕ отдельные записи между ними, поскольку вы потеряете слишком много циклов ЦП, переключая потоки: разбейте его на «разумные» партии. «Разумный» может составлять от 1 до 100 тыс. Записей в зависимости от размера записи и выполняемой вами обработки. Может быть, даже если дать Parallels возможность сделать это для вас.
Учитывая, что вы говорите, что задействовано несколько таблиц, может оказаться, что простое вращение одного потока на исходную таблицу может работать хорошо с блокировкой записи в объект SqlBlukCopy для синхронизации.