Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Winforms 幸存的TPL、委托、线程和调用_Winforms_Multithreading_C# 4.0_Task Parallel Library - Fatal编程技术网

Winforms 幸存的TPL、委托、线程和调用

Winforms 幸存的TPL、委托、线程和调用,winforms,multithreading,c#-4.0,task-parallel-library,Winforms,Multithreading,C# 4.0,Task Parallel Library,我在多线程桌面/windows应用程序中面临严重的死锁问题。我担心我没有在非常异步的环境中使用正确的方法来处理委托。此外,即使我将事件“接收”到调用UI线程中,如果可能的话,我仍然必须在UI线程上调用以查看一些操作。下面,是细节 该应用程序基本上是在线文件存储服务用户的客户端。该服务通过REST调用公开功能。我首先为此类调用创建了托管代码包装DLL,允许.NET使用者创建此DLL的静态实例并调用函数。我将以文件上载操作为例 现在,在包装器中,这里是用于文件上载的公共接口: public Int3

我在多线程桌面/windows应用程序中面临严重的死锁问题。我担心我没有在非常异步的环境中使用正确的方法来处理委托。此外,即使我将事件“接收”到调用UI线程中,如果可能的话,我仍然必须在UI线程上调用以查看一些操作。下面,是细节

该应用程序基本上是在线文件存储服务用户的客户端。该服务通过REST调用公开功能。我首先为此类调用创建了托管代码包装DLL,允许.NET使用者创建此DLL的静态实例并调用函数。我将以文件上载操作为例

现在,在包装器中,这里是用于文件上载的公共接口:

public Int32 UploadFile(FileSystemObject FolderToUploadTo, FileInfo LocalFileAndPath, OperationProgressReportEventHandler onOperationProgressReport, FileSystemObjectUploadCompletedEventHandler onOperationCompleted) {
        Int32 ReplyNumber = 0;
        try {
            var TheOperation = new UploadFileObjectOperation(FolderToUploadTo, LocalFileAndPath, _User.APIKey) {
                onProgressReport = onOperationProgressReport,
                onUploadCompleted = onOperationCompleted
            };

            //Add it to the pool of operations
            OperationPool.Add(TheOperation);

            //Start the operation through the factory
            OperationFactory.StartNew(() => {
                TheOperation.Start();
            });

            //Chain the *actual* TPL Task to flush after usage
            TheOperation.InnerTask.ContinueWith(t => {
                t.Dispose(); //Dispose the inner task
                OperationPool.Remove(TheOperation); //Remove the operation from the pool
                TheOperation = null; //Nullify the Operation                
            });

            ReplyNumber = TheOperation.TaskId;
        }
        catch {
            ReplyNumber = 0;
        }

        return ReplyNumber;
    }
如您所见,引用此DLL的实际UI应用程序将向操作发送进度委托和完成委托。现在,操作本身的主体是:

public class UploadFileObjectOperation : BaseOperation, IDisposable {

    //Store

    public FileSystemObjectUploadCompletedEventHandler onUploadCompleted;

    //Constructors
    //Disposing stuff

    protected override void PerformWork() {
        try {
            //Init the WebClient
            UploadClient.UploadProgressChanged += (UploadProgressChanged_s, UploadProgressChanged_e) => {
                //This is my event in base class being raised
                ReportProgress(UploadProgressChanged_e.ProgressPercentage, UploadProgressChanged_e);
            };

            UploadClient.UploadFileCompleted += (UploadFileCompleted_s, UploadFileCompleted_e) => {
                if (UploadFileCompleted_e.Error != null) {
                    throw new ApplicationException("Upload failed. " + UploadFileCompleted_e.Error.Message);
                }
                JObject JSONLiveObject = JObject.Parse(Encoding.UTF8.GetString(UploadFileCompleted_e.Result));

                if (String.Compare((String)JSONLiveObject["status"], Constants._CONST_RESTRESPONSE_STATUS_VALUE_FAIL, false) == 0) {
                    throw new ApplicationException("Upload response failed. " + (String)JSONLiveObject["result"]["message"]);
                }

                //Eureka! Success! We have an upload!
                //This is my event being raised
                UploadTaskCompleted(new UploadFileObjectOperationEventArg {
                    Error = null,
                    ResultSource = OperationResultSource.Fresh,
                    Status = OperationExitStatus.Success,
                    TaskId = TaskId,
                    UploadedFileSystemObject = _UploadedFile
                });
            };

            //Start the async upload
            UploadClient.UploadFileAsync(AddressOfRESTURI, UploadingMethod, _FileToUpload.FullName);
        }
        catch (OperationCanceledException exp_Canceled) {
            UploadTaskCompleted(new UploadFileObjectOperationEventArg {
                Error = exp_Canceled,
                ResultSource = OperationResultSource.Fresh,
                Status = OperationExitStatus.Canceled,
                TaskId = TaskId,
                UploadedFileSystemObject = _UploadedFile
            });
            // To ensure that the calling code knows the task was canceled
            //throw;
        }
        catch (Exception exp) {
            UploadTaskCompleted(new UploadFileObjectOperationEventArg {
                Error = exp,
                ResultSource = OperationResultSource.Fresh,
                Status = OperationExitStatus.Error,
                TaskId = TaskId,
                UploadedFileSystemObject = _UploadedFile
            });
            // If the calling code also needs to know.
            //throw;
        }
    }

    protected void UploadTaskCompleted(UploadFileObjectOperationEventArg arg) {
        if (onUploadCompleted == null)
            return;

        //Sinking into calling UI thread, if possible
        if (onUploadCompleted.Target is Control) {
            Control targetForm = onUploadCompleted.Target as Control;
            targetForm.Invoke(onUploadCompleted, new object[] { arg });
        }
        else {
            onUploadCompleted(arg);
        }

        Status = OperationRunningStatus.Completed;
    }

}
PerformWork()
引发两个事件:进度报告和完成。请注意,在引发事件时,我检查是否可以获得到调用线程的路由,并直接推送事件,以避免在UI上调用

现在,让我们看看我是如何在桌面客户端中使用上述所有功能的:

private void UploadFile(FileInfo DraggedFileInfo, FileSystemObject ParentDefination) {
    SessionLifetimeStuff.APICore.UploadFile(ParentDefination, DraggedFileInfo,
    (PercentageCompleted) => {
        #region Progress
        this.InvokeEx(f => {
            UpdateTaskProgress(newTaskQueue.OID, PercentageCompleted.Progress, PercentageCompleted);
        });
        #endregion
    }, (Result) => {
        #region Completion
        this.InvokeEx(f => {
            switch (Result.Status) {
                case OperationExitStatus.Success:
                    Console.WriteLine(String.Format("File: {0} uploaded to {1}", Result.UploadedFileSystemObject.DocumentFullname, Result.UploadedFileSystemObject.FolderId));
                    break;
                case OperationExitStatus.Canceled:
                    DialogManager.ShowDialog(DialogTypeEnum.Warning, "Dropbox", "Upload canceled.", null, this);
                    break;
                case OperationExitStatus.Error:
                    DialogManager.ShowDialog(DialogTypeEnum.Error, "Dropbox", "Upload failed.", Result.Error, this);
                    break;
            }
        });
        #endregion
    });
}
我使用在Stackoverflow上找到的扩展方法添加调用功能: 公共静态类InvokeExtensions{

    public static void InvokeEx<T>(this T @this, Action<T> action) where T : Control {
        if (@this.InvokeRequired) {
            @this.Invoke(action, new object[] { @this });
        }
        else {
            if (!@this.IsHandleCreated)
                return;
            if (@this.IsDisposed)
                throw new ObjectDisposedException("@this is disposed.");

            action(@this);
        }
    }

    public static IAsyncResult BeginInvokeEx<T>(this T @this, Action<T> action)
        where T : Control {
        return @this.BeginInvoke((Action)(() => @this.InvokeEx(action)));
    }

    public static void EndInvokeEx<T>(this T @this, IAsyncResult result)
        where T : Control {
        @this.EndInvoke(result);
    }

}
publicstaticvoidinvokeex(this T@this,Action-Action),其中T:Control{
如果(@this.invokererequired){
@调用(action,新对象[]{@this});
}
否则{
如果(!@this.IsHandleCreated)
回来
如果(@this.IsDisposed)
抛出新的ObjectDisposedException(“@this is disposed”);
行动(@this);
}
}
公共静态IAsyncResult BeginInvokeEx(this T@this,Action-Action)
其中T:控制{
返回@this.BeginInvoke((Action)(()=>@this.InvokeEx(Action));
}
公共静态void EndInvokeEx(this T@this,IAsyncResult结果)
其中T:控制{
@this.EndInvoke(result);
}
}
在我的代码中,我已经注释掉了调用,因为我认为我不需要这样做,因为所引发的事件即将发生。然而,我意识到我的UI根本没有做任何事情。因此,我添加了
InvokeEx({code;})
,我的UI开始启动活动

现在,为什么我需要调用

如果我尝试从UI执行不同的操作,最终,我的UI会冻结,尽管应用程序仍能正常运行

我在网站上找到了一篇描述委托用法的老文章,我发现其中包含一个IAsyncResult

有人能告诉我哪里出了问题吗

更新: 好的,在UI上对调用代码进行了注释后,我就没有任何活动了。但是在使用
this.InvokeEx
或在
this.BeginInvokeEx
中包装工作时,我会得到UI更新,但过了一段时间后,出现了两个异常(按此顺序):

  • 在创建窗口句柄之前,无法对控件调用Invoke或BeginInvoke
  • 通过等待任务或访问其exception属性,未观察到任务的异常。结果,未观察到的异常被终结器线程重新抛出

你能把答案作为答案吗?这将有助于其他人寻找解决问题的答案这是一个古老的问题,但我相信我没有找到一个有效的解决办法。不得不用不同的方法重新做。这种方法有太多的漏洞。我得四处看看是什么使这东西起作用的。