Я работаю над библиотекой C #, которая выгружает определенные рабочие задачи в графический процессор с помощью NVIDIA CUDA. Примером этого является добавление двух массивов вместе с использованием методов расширения:
float[] a = new float[]{ ... }
float[] b = new float[]{ ... }
float[] c = a.Add(b);
Работа в этом коде выполняется на графическом процессоре. Тем не менее, я хотел бы, чтобы это было сделано асинхронно, так что только тогда, когда необходим результат, будет выполняться код, выполняющийся в блоке ЦП (если результат еще не завершен на GPU). Для этого я создал класс ExecutionResult, который скрывает асинхронное выполнение. При использовании это выглядит следующим образом:
float[] a = new float[]{ ... }
float[] b = new float[]{ ... }
ExecutionResult res = a.Add(b);
float[] c = res; //Implicit converter
В последней строке программа блокируется, если данные уже готовы. Я не уверен в наилучшем способе реализации этого поведения блокировки в классе ExecutionResult, поскольку у меня не очень много опыта в синхронизации потоков и подобных вещей.
public class ExecutionResult<T>
{
private T[] result;
private long computed = 0;
internal ExecutionResult(T[] a, T[] b, Action<T[], T[], Action<T[]>> f)
{
f(a, b, UpdateData); //Asych call - 'UpdateData' is the callback method
}
internal void UpdateData(T[] data)
{
if (Interlocked.Read(ref computed) == 0)
{
result = data;
Interlocked.Exchange(ref computed, 1);
}
}
public static implicit operator T[](ExecutionResult<T> r)
{
//This is obviously a stupid way to do it
while (Interlocked.Read(ref r.computed) == 0)
{
Thread.Sleep(1);
}
return result;
}
}
Действие, переданное конструктору, является асинхронным методом, который выполняет фактическую работу на GPU. Вложенное действие - это метод асинхронного обратного вызова.
Моя главная задача - как лучше и элегантнее справиться с ожиданием, выполненным в конвертере, а также, если есть более подходящие способы решения проблемы в целом. Просто оставьте комментарий, если есть что-то, что мне нужно уточнить или объяснить дальше.