使用事件在单独的线程上获取任务 运行 的用户输入

Getting user input for a Task running on a separate thread using events

我有一个很长的 运行 任务在单独的线程中运行。此方法报告使用 IProgress 作为进度条。此方法有许多点需要用户输入(数据冲突)。发生这种情况时,我正在使用事件(仅摘录),并修改事件参数以传达结果。

string taskResult = await Task.Run(() =>
{
    cpCloneManager cloner = new cpCloneManager(_progress.ReportProgress, srcProvider, srcProjectID, destProvider, destProjectID, transferType);
    cloner.SynchConflict += Cloner_SynchConflict;
    cloner.CloneProject(destSubscriberId, hsTypesToCopy, true);  
}


private void Cloner_SynchConflict(object sender, SyncConflictEventArgs e)
{
    if (_progress.IsSplashFormVisible) _progress.CloseWaitForm();
    if (e.SrcObject is Role) ResolveRoleConflict(e);
    if (e.SrcObject is Project) throw new ApplicationException("There is already an existing project in the target database created from the source project.");
    if (!_progress.IsSplashFormVisible)
    {
        _progress.ShowWaitForm(mx);
    }
}

void ResolveRoleConflict(SyncConflictEventArgs e)
{

    if (e.DestObject == null) return;
    using (popRoleConflictDialog pRCD = new popRoleConflictDialog(e))
    {
        if (pRCD.ShowDialog() == DialogResult.Cancel)
        {
            setStatusResubmit();
            e.Cancel = true;
        }
    }
}


public class cpCloneManager : IDisposable
{
    public event EventHandler<SyncConflictEventArgs> SynchConflict;
    public virtual void OnSynchConflict(SyncConflictEventArgs e) => SynchConflict.Invoke(this, e);

    public cpCloneManager(IProgress<(ProgressFormCommandEnum, int, string)> Progress, DatabaseProvider srcDbProvider, int SrcProjectID, DatabaseProvider destDbProvider, int DestProjectID, CloneTransferType CopyType)
    {
        //...

    }
    
    public string CloneProject(int TargetSubscriberId, HashSet<Type> HsTypesToClone = null, bool AddAsNew = false)
    {
        //...
        var e_synch = new SyncConflictEventArgs(p_src, p_dest, dest_subscriberId);
        OnSynchConflict(e_synch);
        if (e_synch.result = Result.Copy)
        {
            //...
        }
    }
}

这通常可以正常工作,但是一些用户在我用于获取用户反馈的 winform 渲染中遇到奇怪的效果。这与显示用户反馈表单的上下文有关。这些表单实际上是 devexpress winforms,所以不确定问题是出在 winform 本身还是 devex 皮肤上,但无论哪种方式都需要对其进行排序。

我已经查看了此要求的其他解决方案,包括 ,但这需要大量重构,而且会更冗长。

我的问题是:

  1. 使用事件来获取此反馈是可行的解决方案吗?如果是,我如何确保我的表单能够可靠地呈现?
  2. 如果此解决方案(事件)存在无法解决的问题,那么解决此问题的最佳方法是什么?一个例子将不胜感激。

我假设,Cloner_SynchConflict 是表单中的一个方法。尝试更改它:

private void Cloner_SynchConflict(object sender, SyncConflictEventArgs e)
{
    if (InvokeRequired)
    {
        Invoke((EventHandler<SyncConflictEventArgs>)Cloner_SynchConflict, sender, e);
        return;
    }

    if (_progress.IsSplashFormVisible) _progress.CloseWaitForm();
    if (e.SrcObject is Role) ResolveRoleConflict(e);
    if (e.SrcObject is Project) throw new ApplicationException("There is already an existing project in the target database created from the source project.");
    if (!_progress.IsSplashFormVisible)
    {
        _progress.ShowWaitForm(mx);
    }
}

这里你真的不需要事件,一个简单的回调函数也可以。您传递给 cpCloneManager 构造函数的类似 Func<[Types used in SyncConflictEventArgs], bool> 的内容。无论如何,您应该将调用同步回 UI 线程。 Progress<T> class 自动执行此操作(通过在构造函数中使用 SyncronizationContext cought)。