håller på att bygga en bot till IRC det gick bra tills jag kom till temporär ban funktion som ska bana en user i 10 sekunder från kanalen. Då måste jag ha en timer för varje gång någon blir banad och sedan spara hostmasken så att den vet vad den ska unbana när det har gått 10 sec. Hur bör jag göra? Skapa en egen class eller modul som har hand om detta? Tacksam för funderingar och svar. Är det något som är lite luddigt så fråga vad jag menar :)
Timer
9 svar · 426 visningar · startad av renholm
Finns många lösningar, men ett sätt är såhär:
Skapa en hashtabell bannedUsers. Som nyckel använder du "hostmask" och som värde lagras du en TimeStamp när användaren inte är bannad längre.
När gång någon ska bannas, tar du ett lås på hashtabellen (lås på banedUsers.SyncRoot) och lägger till hostmasken (om den inte redan fanns) med värdet now+10sekunder.
För att se om någon är bannad är det bara att söka i hashtabellen efter hostmasken. Får du null är han inte bannad, får du icke-null fanns ju hostmasken i bannedUsers-listan och han är således bannad.
Sen har du en timer (System.Threading.Timers) som du kör typ varannan sekund. Då tar du lås och går igenom hashtabellen och tar bort alla som har timestamp äldre än nu. (Skapa timern som icke-självstarande och starta om den sist i timer-callbacken.)
Beroende på prestandakrav och noggranhet på tiden en användare ska bannas kan du ju variera hur ofta timern ska köras samt även hur mycket jobb du vill lägga på att optimera låshantering kring hashtabellen.
tackar, ska kika på det
räcker nog om timern körs var 5 sekund, hade funderat på en sådan här lösningen men höll på p trixa med Arrays men hade ingen framgång där :P
hur låser jag hashtabelen? förstod inte riktigt hur jag ska låsa med SyncRoot. Funkar inte att köra banedUsers.Syncroot. Sedan lägger jag till nya värden med .Add ?
hittade hur man gjorde, men får detta fel
See the end of this message for details on invoking
just-in-time (JIT) debugging instead of this dialog box.
************** Exception Text **************
System.InvalidOperationException: Collection was modified; enumeration operation may not execute.
at System.Collections.HashtableEnumerator.MoveNext()
at IRCProtectionBot.mainForm.mainTimer_Tick(Object sender, ElapsedEventArgs e) in C:\Documents and Settings\kristoffer\My Documents\Visual Studio Projects\IRCProtectionBot\mainForm.vb:line 157
koden runt line 157 ser ut så här:
Private Sub mainTimer_Tick(ByVal sender As System.Object, ByVal e As System.Timers.ElapsedEventArgs) Handles mainTimer.Elapsed
Dim s As String
For Each s In unBanList.Keys
Dim tmpDate As DateTime = unBanList(s)
If tmpDate < DateTime.Now Then
wsClient.SendData(wsClient.StringToBytes("MODE #bombzone -b " & s & vbCrLf))
SyncLock unBanList.SyncRoot
unBanList.Remove(s)
End SyncLock
End If
[red]Next[/red]
End Sub
159 är röd....
löst problemet :)
Dim s As Object
For Each s In New ArrayList(unBanList.Keys)
lbStatus.AppendText(s & " " & unBanList(s) & vbCrLf)
If unBanList(s) < DateTime.Now Then
wsClient.SendData(wsClient.StringToBytes("MODE #bombzone -b " & s & vbCrLf))
SyncLock unBanList.SyncRoot
unBanList.Remove(s)
End SyncLock
End If
Next
Ledsen att förstöra din glädje, men din kod är fortfarande trasig. ;-)
Problemet är på raden "For Each s In New ArrayList(unBanList.Keys)".
Du skapar ju en ArrayList som innehåller alla nycklar från hashtabellen. Konstruktorn till ArrayListen kommer då att ropa på HashTable.GetEnumerator för att kunna loopa genom hashtabellen med MoveNext() och lägga till alla dess element till ArrayList:an. Det är inte trådsäkert.
Det här kan hända. Anta att timern slår (på tråd "A") till och ArrayListens konstruktor är mitt i listan. Samtidigt kommer en annan tråd (tråd "B") och lägger till ett element i listan, dvs lägger till en bannad användare. Nu har ju B modifierat hashtabellen, medan A håller på att enumerera igenom den. Anta att tråd A blir schedulerad igen. A kommer då att ropa på HashTableEnumerator.MoveNext(), som kommer kasta det InvalidOperationException du redan sett.
Titta på mitt bifogade exempel. Det går att optimera bort en del av låshanteringen, men jag tror det är snabbt nog. Exemplet är i C#, men du förstår nog ändå.
Det funkar ju inte att bifoga filer. Skit bakom spakarna?? ;) Här kommer den inline istället:
using System;
using System.Diagnostics;
using System.Collections;
using System.Threading;
class Foo
{
Hashtable bannedUsers = new Hashtable();
ArrayList toDelete = new ArrayList();
System.Threading.Timer refreshBannedUsersTimer;
const bool trace = true;
static void Main()
{
(new Foo()).Test();
Console.ReadLine();
}
void Test()
{
// starta timern som refreshar listan över banned users
refreshBannedUsersTimer = new Timer(new TimerCallback(OnRefreshBannedUsersTimer), null, 2000, Timeout.Infinite);
// starta en tråd som slumpvist fyller på med banned users
ThreadPool.QueueUserWorkItem(new WaitCallback(Worker));
}
Random random = new Random();
int antal = 0;
void Worker(object o) // simple function for adding banned users
{
for (int i=0; i < random.Next(1, 5); i++)
{
// dummy-värde för hostmask (ökande räknare)
string hostMaskAsString = antal.ToString();
antal++;
BanUser(hostMaskAsString);
}
Thread.Sleep(random.Next(10, 250));
ThreadPool.QueueUserWorkItem(new WaitCallback(Worker));
}
void BanUser(string hostMaskAsString)
{
Debug.WriteLineIf(trace, "Adding banned user: " + hostMaskAsString);
lock (bannedUsers.SyncRoot)
{
bannedUsers.Add(hostMaskAsString, DateTime.Now.AddSeconds(10));
}
}
bool IsUserBanned(string hostMaskAsString)
{
if (bannedUsers[hostMaskAsString] == null)
{
return false;
}
return true;
}
void OnRefreshBannedUsersTimer(object o)
{
try
{
Debug.WriteLineIf(trace, "timern körs");
toDelete.Clear();
lock (bannedUsers.SyncRoot)
{
// man får inte ta bort samtidigt som man enumererar en
// hashtabell. Bygg upp en lista över det som ska tas
// bort.
DateTime now = DateTime.Now;
IDictionaryEnumerator enumerator = bannedUsers.GetEnumerator();
while (enumerator.MoveNext())
{
if ((DateTime)enumerator.Value < now)
{
toDelete.Add((string)enumerator.Key);
}
}
// nu har vi enumererat klart och kan ta bort ur
// hashtavbellen
foreach (string hostMaskAsString in toDelete)
{
bannedUsers.Remove(hostMaskAsString);
Debug.WriteLineIf(trace, "Tar bort hostmask: " + hostMaskAsString);
}
}
}
finally
{
Debug.WriteLineIf(trace, "************* Antal bannade users: " + bannedUsers.Count);
refreshBannedUsersTimer.Change(2000, Timeout.Infinite);
}
}
}
funkar så bra nu :)
tackar developer