Есть ли способ выполнить атомарную операцию (чтение-обновление-запись) ИЛИ над двумя байтами в С#?

Я просмотрел Метод Interlocked.Or в .NET 5. Он отлично подходит для двух целых значений. Есть ли способ выполнить эквивалент этого для двух отдельных значений байта?

Я просмотрел документацию и вижу, что InterlockedOr8 существует в winnt.h, но его P/вызов не даст хороших характеристик производительности.

Я попытался вызвать Interlocked.CompareExchange<T> следующим образом, просто чтобы посмотреть, могу ли я просто вызвать его вообще с некоторыми значениями байтов:

var map = new byte[268435456];
Interlocked.CompareExchange(ref map[0], (byte)137, (byte)137);

Но я получаю следующую ошибку:

Ошибка CS0452. Тип «byte» должен быть ссылочным типом, чтобы использовать его в качестве параметра «T» в универсальном типе или методе «Interlocked.CompareExchange (ref T, T, T)».


person Alexandru    schedule 01.02.2021    source источник
comment
Вот источник Interlocked.Or: source.dot. net/#System.Private.CoreLib/ (или github.com/dotnet/runtime/blob/)   -  person Chris Yungmann    schedule 01.02.2021
comment
Я добавлю, что Interlocked.CompareExchange в основном компилируется в инструкцию ЦП. Обычно это называется операцией CAS (сравнить и поменять местами), не знаю, почему С# назвал это по-другому. Он очень долго управлялся аппаратным обеспечением, еще до x86.   -  person Zer0    schedule 01.02.2021
comment
Я вижу, что InterlockedOr8 существует в winnt.h. Может ли быть способ P/вызвать его из C#? - конечно, но, вероятно, не с сохранением аспектов производительности, которые вы ищете.   -  person Peter Duniho    schedule 01.02.2021
comment
@PeterDuniho Действительно, P / Invoke, вероятно, сожрет процессор.   -  person Alexandru    schedule 01.02.2021
comment
Правильно. Я имею в виду, что, учитывая, что нативная функция использует компилятор, встроенный для непосредственной генерации необходимой инструкции ЦП, и учитывая, что p/invoke обязательно включает в себя фактические вызовы функций, последнее, даже если бы оно могло быть выполнено для компиляции почти наверняка будет задействована совершенно другая последовательность выполняемых инструкций: всего одна для нативной функции в C/C++ и десятки, если не сотни или тысячи, для p/invoke.   -  person Peter Duniho    schedule 01.02.2021
comment
Я рекомендую вам сузить фокус вашего вопроса. Здесь как минимум три разных вопроса. Решите, на какой вопрос вам действительно нужен ответ, а затем отредактируйте вопрос, чтобы он содержал только это, а также подробную информацию о том, что вы уже пытались решить для этого вопроса.   -  person Peter Duniho    schedule 01.02.2021
comment
@PeterDuniho Хорошо, я изменил вопрос.   -  person Alexandru    schedule 01.02.2021
comment
Есть ли Interlocked.CompareExchange для byte? Нет. Нет, если вы ожидаете, что он будет вести себя как другие (инструкция процессора). Не могли бы вы реализовать другие допустимые вызовы CAS, чтобы сделать присваивание byte атомарным? Конечно. Этот ответ может помочь. Не используйте небезопасные приведения и здесь. Попытка выполнить небезопасный CAS с байтом может уничтожить остальную часть слова (в настоящее время 32-битного или 64-битного) в памяти.   -  person Zer0    schedule 01.02.2021
comment
Одним из способов обхода является сохранение байта в поддерживаемом типе CAS и игнорирование остальных как мусора. Проблема в том, что он не работает с массивами байтов, что делает его довольно бесполезным. И пустая трата памяти. Один байт? Конечно, запихните его во что-нибудь поддерживаемое.   -  person Zer0    schedule 01.02.2021
comment
@ Zer0 В моем случае я не могу. Его заполнение приведет к созданию нового типа, что помешает использованию ссылочного значения массива байтов, которое у меня есть в вопросе, для выполнения быстрых многопоточных операций, когда существует много конфликтов потоков по значениям этого массива.   -  person Alexandru    schedule 01.02.2021
comment
@ChrisYungmann @Zer0 Вы, ребята, случайно не знаете, где исходный код содержит реализацию этой функции CompareExchange, не так ли?   -  person Alexandru    schedule 01.02.2021
comment
Я предполагаю, что эта заблокированная операция является частью какого-то более крупного алгоритма, например. вы перебираете массив байтов и проверяете/изменяете их один за другим. Вместо того, чтобы снова и снова вызывать InterlockedOr8 непосредственно из С#, рассмотрите возможность переписать полный алгоритм на С++, а затем вызывать его только один раз из С#. Тогда вы платите только за стоимость одного thunk. Статья.   -  person John Wu    schedule 01.02.2021
comment
Я не знаю, но не думаю, что исходный код имеет значение. Точная инструкция действительно зависит от архитектуры процессора, но CMPXCHG и подобные являются общими. С# здесь мало что может сделать, так как на самом деле он просто предоставляет вам аппаратные возможности. И это соответствует одной инструкции.   -  person Zer0    schedule 01.02.2021
comment
Не могли бы вы немного объяснить свой алгоритм? Если у вас много конфликтов между потоками, производительность, вероятно, будет невысокой, независимо от того, какую синхронизацию вы используете. В некоторых случаях можно позволить потокам работать с отдельными массивами и объединять результат по завершении.   -  person JonasH    schedule 01.02.2021
comment
Возможно, я смогу написать решение без блокировки для того, что вы хотели, если я очень четко понимаю функциональность. Я уже слышу преждевременную оптимизацию и просто использую аргументы блокировки, но, похоже, вы избегаете Monitor и других примитивов синхронизации по какой-то причине.   -  person Zer0    schedule 01.02.2021
comment
@JohnWu Вы подали мне идею для начала, но теперь я немного заблокирован: stackoverflow.com/questions/65998398/   -  person Alexandru    schedule 01.02.2021