gpt4 book ai didi

c# - 无法从正在运行的Task中执行方法

转载 作者:行者123 更新时间:2023-11-30 18:58:24 32 4
gpt4 key购买 nike

我正在研究ASP.NET MVC项目。
我需要一些时间来解释我的疯狂情况。
我正在尝试从MVC项目向Android和Apple设备发送推送通知。
两者的发送逻辑都是正确的,请不要浪费时间考虑这一点

我面临的灾难是:不会调用负责发送通知的静态类中的静态方法。 (我不是新手程序员,我在C#编程领域有5年以上的经验),但是我无法调用方法。

使您处于问题的上下文中,当我在本地计算机(开发计算机)上执行代码时,将调用并执行此方法,并将通知传递给设备。
当我发布MVC项目并将其部署到我们的服务器时,不会调用静态方法。

我怎么知道该方法没有被调用?
因为我正在登录到文本文件,所以我在第一行放置了一条日志语句
方法和调用该方法之前的日志语句。
将执行调用该方法之前的日志并将其充实到文本文件,但是不会执行静态方法开头的日志!!!!

这是一些代码,然后我将告诉您如何解决此问题。

public interface IHandler<T> where T : IMessage
{
Task Handle(T args);
}

public class RequestAddedAppMonitorHandler : IHandler<RequestAdded>
{
public Task Handle(RequestAdded args)
{
return Task.Factory.StartNew(() =>
{
try
{
GoogleNotification notification = CreateAndroidPartnerAppNotification(deviceId);

// this statment is executed, and the text log file will contains this line
TracingSystem.TraceInformation("Before Send Google Notification");

SendersFacade.PartnerSender.Send(notification).Wait();
}
catch (Exception ex)
{
TracingSystem.TraceException(ex);
}
});
}

private GoogleNotification CreateAndroidPartnerAppNotification(string to)
{
return new GoogleNotification(); // some initialization and creating for the notification object.
}
}


立面课

public static class SendersFacade
{
public static GoogleNotificationSender ClientSender { get; private set; }
public static GoogleNotificationSender PartnerSender { get; private set; }
//public static AppleNotificationSender AppleSender { get; private set; }

static SendersFacade()
{
ClientSender = new GoogleNotificationSender("correct api key");
PartnerSender = new GoogleNotificationSender("correct api key");
//AppleSender = some intialization.
}
}


Google通知发送逻辑

public class GoogleNotificationSender
{
private string _authorizationToken;
private string AuthorizationToken
{
get { return _authorizationToken; }
set
{
if (string.IsNullOrEmpty(value))
throw new InvalidOperationException("authorizationToken must not be null");
_authorizationToken = value;
}
}

public GoogleNotificationSender(string authorizationToken)
{
this.AuthorizationToken = authorizationToken;
}

public async Task Send(GoogleNotification notification)
{
// ATTENTION PLEASE
// This method is not called, and the following line is not fleshed to the log file
TracingSystem.TraceInformation("Inside Send Google notification");

using (HttpClient client = new HttpClient())
{
client.DefaultRequestHeaders.TryAddWithoutValidation("Authorization", "key=" + AuthorizationToken);

string json = notification.GetJson();
StringContent content = new StringContent(json, System.Text.Encoding.UTF8, "application/json");

using (HttpResponseMessage message = await client.PostAsync("https://fcm.googleapis.com/fcm/send", content))
{
message.EnsureSuccessStatusCode();

string resultAsString = await message.Content.ReadAsStringAsync();
GoogleNotificationResult result = JsonConvert.DeserializeObject<GoogleNotificationResult>(resultAsString);

if (result.Failure > 0)
throw new Exception($"Sending Failed : {result.Results.FirstOrDefault().Error}");
}
}
}
}


Google通知类

public class GoogleNotification
{
[JsonProperty("to")]
public string To { get; set; }

[JsonProperty("data")]
public JObject Data { get; set; }

[JsonProperty("notification")]
public JObject Notification { get; set; }

// some other property which is not used at all

internal string GetJson()
{
return JsonConvert.SerializeObject(this,
new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });
}
}


前三天我尝试了什么?

1-将用于调试的DLL(不是具有发布模式的已发布DLL)部署到服务器,这不能解决问题。

2-将 SendersFacade设置为非静态类,并在其上应用单调deisng模式,这也不起作用。

public class SendersFacade
{
public static SendersFacade Instance { get; private set; }

public GoogleNotificationSender ClientSender { get; private set; }
public GoogleNotificationSender PartnerSender { get; private set; }
//public static AppleNotificationSender AppleSender { get; private set; }

static SendersFacade()
{
if (Instance != null)
Instance = new SendersFacade();
}
public SendersFacade()
{
ClientSender = new GoogleNotificationSender("correct api key");
PartnerSender = new GoogleNotificationSender("correct api key");
//AppleSender = some intialization.
}
}


3-尝试将发送逻辑放在自己的Handler类中,这样可以正常工作,我能够从服务器发送通知,但是为什么,在地狱中,以下代码正在工作,但是之前代码不起作用??????????

public interface IHandler<T> where T : IMessage
{
Task Handle(T args);
}

public class RequestAddedAppMonitorHandler : IHandler<RequestAdded>
{
public Task Handle(RequestAdded args)
{
return Task.Factory.StartNew(() =>
{
try
{
GoogleNotification notification = CreateAndroidPartnerAppNotification(deviceId);

// this statment is executed, and the text log file will contains this line
TracingSystem.TraceInformation("Before Send Google Notification");

SendersFacade.PartnerSender.Send(notification).Wait();
}
catch (Exception ex)
{
TracingSystem.TraceException(ex);
}
});
}

private GoogleNotification CreateAndroidPartnerAppNotification(string to)
{
return new GoogleNotification(); // some initialization and creating for the notification object.
}

private void Send(GoogleNotification notification, string authorizationToken)
{
TracingSystem.TraceInformation("Inside Send Google notification");

using (HttpClient client = new HttpClient())
{
client.DefaultRequestHeaders.TryAddWithoutValidation("Authorization", "key=" + authorizationToken);

string json = notification.GetJson();
StringContent content = new StringContent(json, System.Text.Encoding.UTF8, "application/json");

using (HttpResponseMessage message = client.PostAsync("https://fcm.googleapis.com/fcm/send", content).Result)
{
message.EnsureSuccessStatusCode();

string resultAsString = message.Content.ReadAsStringAsync().Result;
GoogleNotificationResult result = JsonConvert.DeserializeObject<GoogleNotificationResult>(resultAsString);

if (result.Failure > 0)
throw new Exception($"Sending Failed : {result.Results.FirstOrDefault().Error}");
}
}
}
}


只需将send方法的逻辑添加到 RequestAddedAppMonitorHandler类即可解决该问题,但是我不想这样做,以及为什么首先发生这种情况?
它只是在调用方法。

3-尝试使send方法成为串行方法(不使用 async),并且它也不起作用

public void Send(GoogleNotification notification)
{
TracingSystem.TraceInformation("Inside Send Google notification");

using (HttpClient client = new HttpClient())
{
client.DefaultRequestHeaders.TryAddWithoutValidation("Authorization", "key=" + AuthorizationToken);

string json = notification.GetJson();
StringContent content = new StringContent(json, System.Text.Encoding.UTF8, "application/json");

using (HttpResponseMessage message = client.PostAsync(BASE_URL, content).Result)
{
message.EnsureSuccessStatusCode();

string resultAsString = message.Content.ReadAsStringAsync().Result;
GoogleNotificationResult result = JsonConvert.DeserializeObject<GoogleNotificationResult>(resultAsString);

if (result.Failure > 0)
throw new Exception($"Sending Failed : {result.Results.FirstOrDefault().Error}");
}
}
}


注意1:
我注意到服务器上出现问题(本机上根本没有出现),该服务器专用于该网站的应用程序池频繁停止,这导致请求该网站时503服务不可用。

注意2:我怀疑最可能的原因是线程问题。
但我无法确定解决方案

注意3:请不要认为这个问题有答案,它根本没有帮助我。

我从三天开始致力于这一工作,我真的无望了,任何想法,谢谢。


更新资料
@Nkosi的答案确实很有帮助,至少我现在知道是什么问题,我决定一路同步。并避免将 async/await与Blocking被调用者混在一起。

所以这是我达到的结果

public class RequestAddedAppMonitorHandler : IHandler<RequestAdded>
{
public Task Handle(RequestAdded args)
{
return Task.Factory.StartNew(() =>
{
try
{
if (deviceOS.Value == DeviceOSEnum.Android.ToString())
{
GoogleNotification notification = CreateAndroidUpdateRequestMessage(args.CustomerRequest, deviceId.Value, notificationString.Title_RequestStared, message);
SendGoogleNotification(notification, "some id");
}
else if (deviceOS.Value == DeviceOSEnum.IOS.ToString())
{
AppleNotification notification = CreateAppleNotification(deviceId.Value, notificationString.Title_RequestStared, message);
AppleNotificationSender sender = new AppleNotificationSender();
sender.SendAppleNotification(notification);
}
}
catch (Exception ex)
{
TracingSystem.TraceException(ex);
}
});
}


和AppleNotificationSender类

public class AppleNotificationSender
{
private TcpClient client;
private string host = "gateway.push.apple.com";
private int port = 2195;
private X509Certificate2 certificate;

public AppleNotificationSender()
{
string path = HostingEnvironment.MapPath("~/Certificates.p12");
certificate = new X509Certificate2(path, "some correct password");
}

private void SetSocketKeepAliveValues(Socket socket, int KeepAliveTime, int KeepAliveInterval)
{
//KeepAliveTime: default value is 2hr
//KeepAliveInterval: default value is 1s and Detect 5 times

uint dummy = 0; //lenth = 4
byte[] inOptionValues = new byte[System.Runtime.InteropServices.Marshal.SizeOf(dummy) * 3]; //size = lenth * 3 = 12

BitConverter.GetBytes((uint)1).CopyTo(inOptionValues, 0);
BitConverter.GetBytes((uint)KeepAliveTime).CopyTo(inOptionValues, System.Runtime.InteropServices.Marshal.SizeOf(dummy));
BitConverter.GetBytes((uint)KeepAliveInterval).CopyTo(inOptionValues, System.Runtime.InteropServices.Marshal.SizeOf(dummy) * 2);
// of course there are other ways to marshal up this byte array, this is just one way
// call WSAIoctl via IOControl

// .net 3.5 type
socket.IOControl(IOControlCode.KeepAliveValues, inOptionValues, null);
}

private bool SocketCanWrite(SslStream stream)
{
if (client == null)
return false;

if (stream == null || !stream.CanWrite)
return false;

if (!client.Client.Connected)
return false;

return client.Client.Poll(1000, SelectMode.SelectWrite);
}

private void Connect()
{
try
{
if (client == null)
client = new TcpClient();

client.Connect(host, port);

//Set keep alive on the socket may help maintain our APNS connection
try { client.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true); }
catch { }

// Really not sure if this will work on MONO....
// This may help windows azure users
try
{
SetSocketKeepAliveValues(client.Client, (int)TimeSpan.FromMinutes(20).TotalMilliseconds, (int)TimeSpan.FromSeconds(30).TotalMilliseconds);
}
catch { }
}
catch (Exception ex)
{
throw new Exception("Failed to Connect, check your firewall settings!", ex);
}
}

public void SendAppleNotification(AppleNotification notification)
{
SslStream stream = null;
try
{
Connect();

stream = new SslStream(client.GetStream(),
false,
(sender, cert, chain, policyErrors) => true,
(sender, targetHost, localCerts, remoteCert, acceptableIssuers) => certificate);

try
{
X509CertificateCollection collection = new X509CertificateCollection();
collection.Add(certificate);
stream.AuthenticateAsClient(host, collection, System.Security.Authentication.SslProtocols.Tls, false);
}
catch (System.Security.Authentication.AuthenticationException ex)
{
throw new Exception("SSL Stream Failed to Authenticate as Client", ex);
}

if (!stream.IsMutuallyAuthenticated)
throw new Exception("SSL Stream Failed to Authenticate", null);

if (!stream.CanWrite)
throw new Exception("SSL Stream is not Writable", null);

if (!SocketCanWrite(stream))
Connect();

byte[] data = notification.ToBytes();
stream.Write(data, 0, data.Length);
//TracingSystem.TraceInformation("Write to stream ended.");
}
catch (Exception)
{
TracingSystem.TraceError("Error in sending Apple notification");
throw;
}
finally
{
try { stream?.Close(); } catch { }
try { stream?.Dispose(); } catch { }
try { client?.Client?.Shutdown(SocketShutdown.Both); } catch { }
try { client?.Client?.Dispose(); } catch { }
try { client?.Close(); } catch { }
client = null;
}
}
}


现在,我解决了死锁问题,但是又遇到了另一个问题。发送Apple通知时,触发此 Handle方法的MVC操作将被调用两次,这将导致业务规则异常(如果此操作被触发两次,则是正常现象)。并且根本没有到达Apple通知。
 注意:当我调试在本地计算机上发送Apple Notification的代码时,一切正常,通知已到达,并且Action仅调用一次,将代码部署到服务器后,才会出现上述问题。 。
 注意:完全发送Google通知时,不会出现此问题。

顺便说一下,这是Handle方法的触发

public class MessageBus : ICommandSender
{
public static MessageBus Instance { get; private set; }

private MessageBus()
{
handlers = new List<Delegate>();
}

static MessageBus()
{
if (Instance == null)
Instance = new MessageBus();
}

private List<Delegate> handlers;

public void Send<T>(T command) where T : ICommand
{
List<Task> tasks = new List<Task>();
foreach (Func<T, Task> handle in handlers.OfType<Func<T, Task>>())
{
try { tasks.Add(handle(command)); }
catch (Exception ex) { TracingSystem.TraceException(ex); }
}

try { Task.WaitAll(tasks.ToArray()); }
catch (BusinessRuleException buzEx) { TracingSystem.TraceException(buzEx); throw buzEx; }
catch (Exception ex) { TracingSystem.TraceException(ex); }
}
}

最佳答案

但是,为什么下面的代码在起作用,但是之前的代码在起作用?


正常工作的代码之所以有效,是因为它们都是同步调用的,并且没有混合异步/等待和阻塞调用。

在前面的代码中,您将async/await与阻塞调用(例如.Result.Wait())混合使用,这可能导致死锁。您要么一直处于异步状态,要么一直处于同步状态。

我建议您重构GoogleNotificationSender,并确保它一直都是异步的

public class GoogleNotificationSender {
private HttpClient client;
private string authorizationToken;

public GoogleNotificationSender(string authorizationToken) {
this.AuthorizationToken = authorizationToken;
}

private string AuthorizationToken {
get { return authorizationToken; }
set {
if (string.IsNullOrEmpty(value))
throw new InvalidOperationException("authorizationToken must not be null");
authorizationToken = value;
}
}

private HttpClient Client {
get {
if (client == null) {
client = new HttpClient();
client.DefaultRequestHeaders.TryAddWithoutValidation("Authorization", "key=" + AuthorizationToken);
}
return client;
}
}

public async Task SendAsync(GoogleNotification notification) {
TracingSystem.TraceInformation("Inside Send Google notification");

var json = notification.GetJson();
var content = new StringContent(json, System.Text.Encoding.UTF8, "application/json");
var requestUri = "https://fcm.googleapis.com/fcm/send";

using (var message = await Client.PostAsync(requestUri, content)) {
message.EnsureSuccessStatusCode();

var result = await message.Content.ReadAsAsync<GoogleNotificationResult>();
if (result.Failure > 0)
throw new Exception($"Sending Failed : {result.Results.FirstOrDefault().Error}");
}
}
}


请注意将 Send重命名为 SendAsync以正确表达意图。另外,请尝试不要在每个呼叫上创建一个新的 HttpClient。那可能会有副作用,但这超出了此问题和答案的范围。关于SO的解释已经有很多答案。

接下来确保Handler也已正确实现为异步

public class RequestAddedAppMonitorHandler : IHandler<RequestAdded> {
public async Task Handle(RequestAdded args) {
try {
string deviceId = args.DeviceId;//This is an assumption here
var notification = CreateAndroidPartnerAppNotification(deviceId);

// this statment is executed, and the text log file will contains this line
TracingSystem.TraceInformation("Before Send Google Notification");

await SendersFacade.PartnerSender.SendAsync(notification);
} catch (Exception ex) {
TracingSystem.TraceException(ex);
}
}

private GoogleNotification CreateAndroidPartnerAppNotification(string to) {
// some initialization and creating for the notification object.
return new GoogleNotification() {
To = to
};
}
}


最后,请尝试确保在调用堆栈中没有阻塞呼叫,因为这只会使您重新陷入所遇到的死锁问题。即:调用 Task IHandler<T>.Handle(T args)的内容不应混用异步和阻塞的调用。

如果无法完全理解异步/等待,您应该考虑阅读

Async/Await - Best Practices in Asynchronous Programming

以更好地理解该主题。

关于c# - 无法从正在运行的Task中执行方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45031989/

32 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com