C#Task <>使绑定到对象的事件不触发

时间:2018-07-19 22:10:58

标签: c# asynchronous events task

所以...我正在使用第三方库(zkemkeeper.dll)进行Winform应用程序来管理考勤设备。我正在尝试连接多个设备并附加一些事件,以侦听它们将触发的某些操作。一切正常都可以运行。我运行一些方法,并为每个设备尝试这样连接:

public bool connect(Device myDevice)
{
     bool result=false;
     if (!myDevice.isConn)
     {
        myDevice.zkDevice = null;
        myDevice.zkDevice = new CZKEMClass();
        myDevice.zkDevice.MachineNumber = myDevice.id;
        if (myDevice.password.HasValue)
        {
            myDevice.zkDevice.SetCommPassword(myDevice.password.Value);
        }
        try
        {
            result = myDevice.zkDevice.Connect_Net(myDevice.ip, myDevice.port);
            myDevice.error = result ? "" : "Could not connect to device";
        }
        catch (Exception ex)
        {
            myDevice.error = ex.Message;
            result = false;
        }
        if(result)
        {
            //Bind events
            if (myDevice.zkDevice.RegEvent(myDevice.id, 1))
            {
                myDevice.zkDevice.OnAttTransactionEx += new _IZKEMEvents_OnAttTransactionExEventHandler(device_OnTransactionEx);
            }
        }
        myDevice.zkDevice.EnableDevice(myDevice.id, true);
        myDevice.zkDevice.EnableClock(1);
    }
        return result;
}

我的主要问题是,这取决于网络的状态,需要花费几秒钟时间才能连接到每个设备,因此,如果我有50或100个设备,则每次连接或重新连接设备时接口都会冻结,并且会造成很大的延迟。(除了该应用程序必须始终可以运行之外)

好了,我要使用它:

private async Task connectAllAsync()
{
    List<Task<bool>> lstTasks = new List<Task<bool>>();

    foreach (var device in lstDevices)
    {
        lstTasks.Add(Task.Run(() => connect(device)));
    }

    var arrayComplete =await Task.WhenAll(lstTasks);
    int countErr=arrayComplete.ToList().Where(n => n == false).Count();
    if(countErr>0)
    {
        timerReconnect.Enabled = true;
    }

}

此界面不会冻结之后,即使我尝试询问任何设备以获取信息,但设备尝试响应任何未触发的事件时,我对所有设备的连接都会更快。,我不知道我的错误在哪里,我认为这可能与在另一个线程上进行该事件或类似的事件有关。。。谢谢我,给我一些方法。

编辑:我还尝试使“ connect”功能异步,使其等待Connect_Net响应(这是第三方代码的一部分)

1 个答案:

答案 0 :(得分:0)

您有未被捕获的closure。试试这个:

foreach (var device in lstDevices)
{
    var captured = device;
    lstTasks.Add(Task.Run(() => connect(captured)));
}

或者只需使用LINQ即可完成所有操作:

lstTasks = lstDevices.Select
( 
    device => Task.Run
    ( 
        () => connect(device) 
    ) 
);

一种完全不同的方法将摆脱Task.Run()并改用Parallel.ForEach。如果使用这种方法,则必须将结果存储在线程安全的容器中,例如ConcurrentBag<>

var results = new ConcurrentBag<bool>();
Parallel.ForEach
(
    lstDevices, 
    device =>
    {
        results.Add(connect(device));
    }
);
int countErr = results.Count( x => x == false );

或者您也可以使用一个计数器:

volatile int countErr = 0;
Parallel.ForEach
(
    lstDevices, 
    device => 
    {
        var ok = results.Add(connect(device));
        if (!ok) Interlocked.Increment(ref countErr);
    }
);