Есть объект, который может обрабатывать только один запрос за раз, и для его обработки требуется немного времени. После выполнения задачи вызывается событие, возвращающее результат. Объект Computer
в следующем коде, и допустим, я не могу изменить поведение этого класса.
Теперь я хочу создать класс-оболочку, чтобы создать у клиентов впечатление, что они могут отправить запрос в любое время. Запрос теперь является асинхронным методом, так что клиент может просто ждать, пока не будет возвращен результат. Конечно, базовый объект может обрабатывать один запрос за раз, поэтому оболочке необходимо поставить запрос в очередь, а когда приходит событие завершения обработки, он должен вернуть результат соответствующему клиенту. Этот класс-оболочка SharedComputer
в следующем коде.
Я думаю, мне нужно вернуть значение, которое я получил от Place1 в Place2. Какова рекомендуемая практика для этого? Разве у BufferBlock/ActionBlock нет механизма для возврата значения?
static void Main(string[] args)
{
SharedComputer pc = new SharedComputer();
for(int i =0; i<10; i++)
{
Task.Factory.StartNew(async() =>
{
var r = new Random();
int randomDelay = r.Next(500, 5000);
Thread.Sleep(randomDelay);
int random1 = r.Next(0, 10);
int random2 = r.Next(0, 10);
int sum = await pc.Add(random1, random2);
if(random1 + random2 == sum)
{
Debug.WriteLine($"Got correct answer: {random1} + {random2} = {sum}.");
}
else
{
Debug.WriteLine($"Got incorrect answer: {random1} + {random2} = {sum}.");
}
});
}
System.Console.Read();
}
}
class SharedComputer
{
Computer Mainframe= Computer.GetInstance();
BufferBlock<TwoNumbers> JobQueue = new BufferBlock<TwoNumbers>();
TaskCompletionSource<int> TCS;
public SharedComputer()
{
Mainframe.Calculated += Mainframe_Calculated;
var options = new ExecutionDataflowBlockOptions
{
MaxDegreeOfParallelism = 1
};
var jobProcessor = new ActionBlock<TwoNumbers>(async e =>
{
Debug.WriteLine("Starting an adding");
TCS = new TaskCompletionSource<int>();
Mainframe.StartAdding(e.A, e.B);
var res = await TCS.Task; // Place1
Debug.WriteLine("Got the result.");
}, options);
JobQueue.LinkTo(jobProcessor);
}
private void Mainframe_Calculated(object sender, int e)
{
TCS.SetResult(e);
}
public async Task<int> Add(int a, int b)
{
var data = new TwoNumbers()
{
A = a,
B = b
};
Debug.WriteLine("Queuing a new adding.");
JobQueue.Post<TwoNumbers>(data);
return 1;//Place2: Return the value received at Place1.
}
struct TwoNumbers
{
public int A;
public int B;
}
}
class Computer
{
static Computer Instance;
bool IsWorking = false;
private Computer()
{
}
public static Computer GetInstance()
{
if (Instance == null)
Instance = new Computer();
return Instance;
}
public event EventHandler<int> Calculated;
public void StartAdding(int a, int b)
{
if (IsWorking)
{
throw new InvalidOperationException("Already working.");
}
IsWorking = true;
Task.Factory.StartNew(() =>
{
Thread.Sleep(3000);
IsWorking = false;
Calculated(this, a + b);
});
}
}