停止挂同步方法

时间:2017-11-29 11:03:02

标签: c# multithreading asynchronous task cancellation

XenAPI中有一种方法HTTP_actions.put_import(),它是同步的,它支持通过其代理取消

我有以下方法:

onRowSelected:(params) => {
    if(params.data.yourProperty && params.node.isSelected())
        params.node.setSelected(false);
}

在某些情况下,此方法private void UploadImage(.., Func<bool> isTaskCancelled) { try { HTTP_actions.put_import( cancellingDelegate: () => isTaskCancelled(), ...); } catch (HTTP.CancelledException exception) { } } 会挂起并且不会对HTTP_actions.put_import做出反应。在这种情况下,整个应用程序也会挂起。

我可以在一个单独的线程中运行此方法,并在收到取消信号后强行终止它,但此方法并不总是挂起,有时我想优雅地取消此方法。只有当这种方法真的悬挂时,我​​才想自己杀掉它。

处理这种情况的最佳方法是什么?

3 个答案:

答案 0 :(得分:3)

撰写以下博文:http://pranayamr.blogspot.in/2017/12/abortcancel-task.html

自从你最近2小时以来尝试了很多解决方案,我想出了以下工作解决方案,请试试看

class Program
{
   //capture request running that , which need to be cancel in case
   // it take more time 
    static Thread threadToCancel = null;
    static async Task<string> DoWork(CancellationToken token)
    {
        var tcs = new TaskCompletionSource<string>();
        //enable this for your use
    //await Task.Factory.StartNew(() =>
    //{
    //    //Capture the thread
    //    threadToCancel = Thread.CurrentThread;
    //    HTTP_actions.put_import(...); 
    //});
    //tcs.SetResult("Completed");
    //return tcs.Task.Result;

    //comment this whole this is just used for testing 
        await Task.Factory.StartNew(() =>
        {
            //Capture the thread
            threadToCancel = Thread.CurrentThread;

            //Simulate work (usually from 3rd party code)
            for (int i = 0; i < 100000; i++)
            {
                Console.WriteLine($"value {i}");
            }

            Console.WriteLine("Task finished!");
        });

        tcs.SetResult("Completed");
        return tcs.Task.Result;
    }


    public static void Main()
    {
        var source = new CancellationTokenSource();
        CancellationToken token = source.Token;
        DoWork(token);
        Task.Factory.StartNew(()=>
        {
            while(true)
            {
                if (token.IsCancellationRequested && threadToCancel!=null)
                {
                    threadToCancel.Abort();
                    Console.WriteLine("Thread aborted");
                }
            }
        });
        ///here 1000 can be replace by miliseconds after which you want to 
        // abort thread which calling your long running method 
        source.CancelAfter(1000);
        Console.ReadLine();   
    }
}

答案 1 :(得分:0)

HTTP_actions.put_import 电话 HTTP_actions.put 电话 HTTP.put 电话 HTTP.CopyStream

委托传递给CopyStream,然后检查函数是否为空(未传递)或为真(返回值)。但是,它只在While语句中执行此操作,因此Read的{​​{1}}可能导致阻塞操作。虽然如果使用Stream,它也可能出现在progressDelegate中。

要解决此问题,请将HTTP.put_import()的调用置于任务或后台线程中,然后单独检查取消或从任务/线程返回。

有趣的是,快速浏览一下CopyStream代码就会发现一个错误。如果一个进程被取消的函数返回一个不同的值,基于它正在进行的一些检查,你实际上可以让循环退出而不生成CancelledException()CancelledException调用的结果应存储在局部变量中。

答案 2 :(得分:0)

这是我的最终实施(基于Pranay Rana的答案)。

public class XenImageUploader : IDisposable
{
    public static XenImageUploader Create(Session session, IComponentLogger parentComponentLogger)
    {
        var logger = new ComponentLogger(parentComponentLogger, typeof(XenImageUploader));

        var taskHandler = new XenTaskHandler(
            taskReference: session.RegisterNewTask(UploadTaskName, logger),
            currentSession: session);

        return new XenImageUploader(session, taskHandler, logger);
    }

    private XenImageUploader(Session session, XenTaskHandler xenTaskHandler, IComponentLogger logger)
    {
        _session = session;
        _xenTaskHandler = xenTaskHandler;
        _logger = logger;

        _imageUploadingHasFinishedEvent = new AutoResetEvent(initialState: false);

        _xenApiUploadCancellationReactionTime = new TimeSpan();
    }

    public Maybe<string> Upload(
        string imageFilePath,
        XenStorage destinationStorage,
        ProgressToken progressToken,
        JobCancellationToken cancellationToken)
    {
        _logger.WriteDebug("Image uploading has started.");

        var imageUploadingThread = new Thread(() =>
            UploadImageOfVirtualMachine(
                imageFilePath: imageFilePath,
                storageReference: destinationStorage.GetReference(),
                isTaskCancelled: () => cancellationToken.IsCancellationRequested));

        imageUploadingThread.Start();

        using (new Timer(
            callback: _ => WatchForImageUploadingState(imageUploadingThread, progressToken, cancellationToken),
            state: null,
            dueTime: TimeSpan.Zero,
            period: TaskStatusUpdateTime))
        {
            _imageUploadingHasFinishedEvent.WaitOne(MaxTimeToUploadSvm);
        }

        cancellationToken.PerformCancellationIfRequested();

        return _xenTaskHandler.TaskIsSucceded
            ? new Maybe<string>(((string) _xenTaskHandler.Result).GetOpaqueReferenceFromResult())
            : new Maybe<string>();
    }

    public void Dispose()
    {
        _imageUploadingHasFinishedEvent.Dispose();
    }

    private void UploadImageOfVirtualMachine(string imageFilePath, XenRef<SR> storageReference, Func<bool> isTaskCancelled)
    {
        try
        {
            _logger.WriteDebug("Uploading thread has started.");

            HTTP_actions.put_import(
                progressDelegate: progress => { },
                cancellingDelegate: () => isTaskCancelled(),
                timeout_ms: -1,
                hostname: new Uri(_session.Url).Host,
                proxy: null,
                path: imageFilePath,
                task_id: _xenTaskHandler.TaskReference,
                session_id: _session.uuid,
                restore: false,
                force: false,
                sr_id: storageReference);

            _xenTaskHandler.WaitCompletion();

            _logger.WriteDebug("Uploading thread has finished.");
        }
        catch (HTTP.CancelledException exception)
        {
            _logger.WriteInfo("Image uploading has been cancelled.");
            _logger.WriteInfo(exception.ToDetailedString());
        }

        _imageUploadingHasFinishedEvent.Set();
    }

    private void WatchForImageUploadingState(Thread imageUploadingThread, ProgressToken progressToken, JobCancellationToken cancellationToken)
    {
        progressToken.Progress = _xenTaskHandler.Progress;

        if (!cancellationToken.IsCancellationRequested)
        {
            return;
        }

        _xenApiUploadCancellationReactionTime += TaskStatusUpdateTime;

        if (_xenApiUploadCancellationReactionTime >= TimeForXenApiToReactOnCancel)
        {
            _logger.WriteWarning($"XenApi didn't cancel for {_xenApiUploadCancellationReactionTime}.");

            if (imageUploadingThread.IsAlive)
            {
                try
                {
                    _logger.WriteWarning("Trying to forcefully abort uploading thread.");

                    imageUploadingThread.Abort();
                }
                catch (Exception exception)
                {
                    _logger.WriteError(exception.ToDetailedString());
                }
            }

            _imageUploadingHasFinishedEvent.Set();
        }
    }

    private const string UploadTaskName = "Xen image uploading";

    private static readonly TimeSpan TaskStatusUpdateTime = TimeSpan.FromSeconds(1);
    private static readonly TimeSpan TimeForXenApiToReactOnCancel = TimeSpan.FromSeconds(10);
    private static readonly TimeSpan MaxTimeToUploadSvm = TimeSpan.FromMinutes(20);

    private readonly Session _session;
    private readonly XenTaskHandler _xenTaskHandler;
    private readonly IComponentLogger _logger;

    private readonly AutoResetEvent _imageUploadingHasFinishedEvent;

    private TimeSpan _xenApiUploadCancellationReactionTime;
}