开发者

C# - how to make a sequence of method calls atomic?

I have to make a sequence of method calls in C# such t开发者_如何学编程hat, if one of them fails, the subsequent methods should not be called. In short, the set of calls should be made atomic. How do I achieve this in C#?


I think you're confusing the word "atomic" with something else. Atomic is when an operation cannot be interrupted and is usually done in multi threaded scenarios to protect shared resources.

What you want is normal control flow logic and the solution depends on what your methods looks like.

One solution could be to have them return a boolean indicating whether or not it succeeded:

bool success = false;

success = MethodA();
if (!success)
  return;
success = MethodB();
if (!success)
  return;

// or even like this as suggested in another answer
if (MethodA() &&
    MethodB() &&
    MethodC())
{
  Console.WriteLine("All succeeded");
}

You could also use exceptions and wrap all your method calls inside a try-catch block. If one of them fails (throws an exception), your catch block will execute and nothing after that method call in the try-block will get a chance to run.

try
{
  MethodA();
  MethodB();
  MethodC();
}
catch (MyMethodFailedException)
{
  // do something clever
}

If you need rollback functionality, you have to get into transactions but that's a whole bigger topic.


TransactionScope might be what you need see here

void RootMethod()
{
     using(TransactionScope scope = new TransactionScope())
     {
          /* Perform transactional work here */
          SomeMethod();
          SomeMethod2();
          SomeMethod3();
          scope.Complete();
     }
}


Preferably, get them to throw an exception when they fail and write your call sequence in a try/catch block.

If you can't do that for some reason, get them to return true on success and use &&:

if (a() && b() && c())
{
    ....

(That's not "atomic" in the true sense of the word, but I don't think you're asking for true atomicity.)


if one of them fails, the subsequent methods should not be called. In short, the set of calls should be made atomic.

That is not what atomic means. As other answers have explained you can achieve this simply by checking the result of each method call and stopping when you get a certain result.

Atomic means that either all of the methods are called or none of them are. So you guarantee that the entire block runs or doesn't run at all. This is not something you can achieve in C# code, or in most languages that I'm aware of.

What you would have to do is separate checking results from final processing and queue up the final processing instead.

So instead of:

bool Method1() {
   if (CheckCondition)
      return false;
   DoSomethingImportant();
   return true;
}

bool Method2() {
   if (CheckCondition)
      return false;
   DoSomethingElseImportant();
   return true;
}

...

var success1 = Method1();
if (!success1)
   return;

var success2 = Method2();
if (!success2)
   return; // oops we already did something important in method1

Do something like:

bool Method1() {
   if (CheckCondition)
      return false;
   queue.Enqueue(DoSomethingImportant);
   return true;
}

bool Method2() {
   if (CheckCondition)
      return false;
   queue.Enqueue(DoSomethingElseImportant);
   return true;
}

...

var success1 = Method1();
if (!success1)
   return;

var success2 = Method2();
if (!success2)
   return; // no problem, nothing important has been invoked yet

// ok now we're completely successful, so now we can perform our important actions

while (queue.Any()) {
   var doSomethingImportant = queue.Dequeue();
   doSomethingImportant();
}

This still isn't anywhere's close to being actually "atomic", but it does give you a very basic "all or nothing" effect.


If you are not catching exceptions, then if you throw an exception, all other methods called abort until a try block is found. So simply throw an exception where you need to have the atomic calls end (e.g. when it fails) and then catch it when you need to return to normal rutine.


Here is a rough example of emulating a move operation with compensation if things go wrong. With exceptions thrown from your device copy methods on failure

string source = @"C:\file.txt", dest = @"D:\file.txt";

bool fileCopied = false;
try
{
    DeviceCopy(source, dest);
    fileCopied = true;
    DeviceDelete(source);
}
catch
{
    if (fileCopied)
    {
        DeviceDelete(dest);
    }
    throw;
}

Or with error codes e.g. could be bool for failed or check an integer

if (DeviceCopy(source, dest))
{
    if (!DeviceDelete(source))
    {
        if (!DeviceDelete(dest))
        {
            throw new IOException("Oh noes");
        }
    }
}


Are you thinking of multicast delegates? Something like:

delegate void SomeFunc(int x);

void foo() {
    SomeFunc funcSeq = new SomeFunc(func1);
    funcSeq += new SomeFunc(func2);
    funcSeq(100);   // Invokes func1 and func2
}

void func1(int foo) {
    // Use foo
}

void func2(int bar) {
    // Use bar
}

This won't work, because the order that the functions is called in is undefined. Plus, since your delegate functions can't return a value, there's no way to indicate that they failed, except by throwing an exception. And if you're going to throw an exception, you're better off using one of the earlier suggestions, as then your order of execution is defined (and your methods don't all have to have the same signature).

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜