在C#中处理大量任务

时间:2015-07-24 17:44:33

标签: c# winforms datagridview task-parallel-library bindinglist

我有一个BindingList<>绑定到DataGridView的对象。这些对象包含另一个对象的外键,第一个对象的某些列用于显示外键对象的只读字段。

我以为我可以将外部对象的副本保存为私有变量,如果这是null,我会向webservice发出获取请求以获取外部对象,然后在相关字段上调用NotifyPropertyChanged()指示已加载外部对象并更新DataGridView中的这些字段。

这很有效 - 至关重要。对于大量记录,似乎某些任务运行并且永远不会返回,即使取消也是如此。有没有更好的方法呢?

    private ForeignObj _foreignObject = null;
    public string ForeignName
    {
        get
        {
            if (!_foreignKey.HasValue)
                return null;

            if (_foreignObject == null)
            {
                GetForeignObject(); // method is async, so it spawns a task and returns immediately
                return "Fetching...";
            }

            return _foreignObject.Name;
        }
    }

    CancellationTokenSource foreignObjectToken = null;
    public async Task GetForeignObject()
    {
        if (foreignObjectToken != null)
            return; // Don't call it again while it is already fetching

        foreignObjectToken = new CancellationTokenSource();
        foreignObjectToken.CancelAfter(10000); // Don't let request go longer than 10 seconds

        _foreignObject = await DTOPropertyHelper.GetForeignObject(_foreignKey.Value, foreignObjectToken.Token); // HTTP request to web service to obtain foreign object

        OnPropertyChanged("ForeignName");
        foreignObjectToken = null;
    }

更新:我发现这个'有效'在属性getter中,但是很难看:

get
{
    if (!_foreignKey.HasValue)
        return null;

    if (_foreignObject == null)
    {
        if (DTOPropertyHelper.NumTasks < DTOPropertyHelper.MaxTasks)
            GetForeignObject();
        else
            OnPropertyChanged("ForeignName");

        return null;
    }

    return _foreignObject.Name;
}

...基本上不断触发PropertyChanged事件,该事件继续锤击任务创建,提供少于MaxTasks的数字是有效的(DTOPropertyHelper.GetForeignObject()自动递增和递减NumTasks)。

1 个答案:

答案 0 :(得分:0)

我假设您有更多属性,例如ForeignName,显示_foreignObject的字段?然后,您在创建foreignObjectToken和检查null之间有一个竞争条件,因为您的async方法已经是Task(意味着它)在自己的线程中运行)。如果是这种情况,您应该使用foreignObjectToken保护lock

Object foreignObjectTokenLock = new Object();
CancellationTokenSource foreignObjectToken = null;
public async Task GetForeignObject()
{
    lock (foreignObjectTokenLock)
    {
        if (foreignObjectToken != null)
            return; // Don't call it again while it is already fetching
        foreignObjectToken = new CancellationTokenSource();
    }
    foreignObjectToken.CancelAfter(10000); // Don't let request go longer than 10 seconds

    _foreignObject = await DTOPropertyHelper.GetForeignObject(_foreignKey.Value, foreignObjectToken.Token); // HTTP request to web service to obtain foreign object

    OnPropertyChanged("ForeignName");
    // I guess there should be some more OnPropertyChanged()?
    foreignObjectToken = null;
}

我必须补充一点,我不清楚这种竞争条件会导致任务根本没有返回(除了Web服务调用可能会以某种方式永久地挂起),所以也许这还不是整个解决方案。