I have the following setup of TransformBlock
:
private void SetupTestModule()
{
Func<int, int> func1 = new Func<int, int>(input =>
{
return (input + 1);
});
Func<int, int> func2 = new Func<int, int>(input =>
{
return (input + 2);
});
TransformBlock<int, int> transform = new TransformBlock<int, int>(func1);
}
I wonder whether I can, during run-time, exchange func1 for func2 and whether all the linkages between this transformBlock and other data blocks will remain intact? Ideally upon swapping the Funcs I simply want the new transformation to apply to all newly incoming items. Obviously with this simplistic approach I would not make assumptions about items that are currently in the in-queue and how they are handled. I just wonder whether assigning a new Func would cause a runtime error or otherwise cause the transformBlock to be unlinked. Anyone who could share some insights?
Edit: Post Jon's suggestions and code here some very basic test code. What makes me curious is that it works with and without the volatile
keyword. Why do we need volatile
?
public class TransformBlockHotSwap
{
private TransformBlock<int, int> transformBlock;
private ActionBlock<int> actionBlock;
public TransformBlockHotSwap()
{
SwappableFunction<int, int> swappable = new SwappableFunction<int, int>(item => item + 1);
transformBlock = new TransformBlock<int, int>(item => swappable.Execute(item));
actionBlock = new ActionBlock<int>(item => Console.WriteLine(item));
transformBlock.LinkTo(actionBlock);
Func<int, int> func2 = new Func<int,int>(item => item * item);
for (int index = 1; index <= 100; index++)
{
transformBlock.Post(index);
Thread.Sleep(500);
if (index == 5)
{
swappable.Swap(func2);
}
}
}
}
public class SwappableFunction<TInput, TOutput>
{
private Func<TInput, TOutput> func;
public SwappableFunction(Func<TInput, TOutput> func)
{
this.func = func;
}
public void Swap(Func<TInput, TOutput> newFunc)
{
func = newFunc;
}
public TOutput Execute(TInput input)
{
return func(input);
}
}
Edit (to include predicate swapping):
public class TransformBlockHotSwap
{
private TransformBlock<int, int> transformBlock;
private ActionBlock<int> actionBlock;
public TransformBlockHotSwap()
{
Func<int, int> defaultFunction = new Func<int, int>(item => item);
Func<int, int> func2 = new Func<int, int>(item => item * item);
Predicate<int> defaultPredicate = new Predicate<int>(item => true);
Predicate<int> pred2 = new Predicate<int>(item =>
{
if (item % 2 == 0)
{
return true;
}
else
{
return false;
}
});
SwappableFunction<int, int> swappableFunction = new SwappableFunction<int, int>(defaultFunction);
SwappablePredicate<int> swappablePredicate = new SwappablePredicate<int>(defaultPredicate);
transformBlock = new TransformBlock<int, int>(item => swappableFunction.Execute(item));
actionBlock = new ActionBlock<int>(item => Console.WriteLine(item));
transformBlock.LinkTo(actionBlock, item => swappablePredicate.Execute(item));
for (int index = 1; index <= 100; index++)
{
transformBlock.Post(index);
if (index == 10)
{
swappablePredicate.Swap(pred2);
}
Thread.Sleep(200);
}
Console.WriteLine("Done");
Console.ReadKey();
}
}
public class SwappableFunction<TInput, TOutput>
{
private volatile Func<TInput, TOutput> func;
public SwappableFunction(Func<TInput, TOutput> defaultFunction)
{
this.func = defaultFunction;
}
public void Swap(Func<TInput, TOutput> newFunc)
{
func = newFunc;
}
public TOutput Execute(TInput input)
{
return func(input);
}
}
public class SwappablePredicate<TInput>
{
private volatile Predicate<TInput> predicate;
public SwappablePredicate(Predicate<TInput> defaultPredicate)
{
this.predicate = defaultPredicate;
}
public void Swap(Predicate<TInput> newPredicate)
{
predicate = newPredicate;
}
public bool Execute(TInput input)
{
return predicate(input);
}
}
I wouldn't expect you to be able to do so - but you could easily write a function which delegates:
public class SwappableFunction<TInput, TOutput>
{
private volatile Func<TInput, TOutput> func;
public SwappableFunction(Func<TInput, TOutput> func)
{
this.func = func;
}
public void Swap(Func<TInput, TOutput> newFunc)
{
func = newFunc;
}
public TOutput Execute(TInput input)
{
return func(input);
}
}
Then:
var swappable = new SwappableFunction<int, int>(input => input + 1);
var block = new TransformBlock<int, int>(swappable.Execute);
// Later...
swappable.Swap(input => input + 2);
Important - I'm not 100% sure about the use of volatile
here - I generally don't like using volatile
as its semantics are confusing. It's possible that using Interlocked.CompareExchange
would be better - but the code would be significantly longer, and I wanted to get the main point across first :)