The problem is likely to be processor allocation - once the script starts, nothing else gets a look in. I read in one of the tutorials that you need to put a delay in the loop to give the game engine an opportunity to do other things. For a one second delay:
#start
{_x whatever} ForEach whoever
~1
goto "start"
Experiment to get the longest workable pause.
As a point of interest, I've used the following init line on soldier units:
this AddEventHandler ["Hit", {_this select 0 setdamage 0.1}]
In theory, each time an individual gets hit, they get hit their damage reset. In practice, two factors seem to affect the ideal: how much 'overkill' the striking round has and the rate at which he's hit; but it does mean these units are slightly killable. With 5.56 and 7.62 (all I've tried it with), a clean headshot usually does the trick before the damage can be reset. Now, my zombie lore is limited, but this is a close approximation to decapitation in my book!
Anyway, why not try something like this in your script (I haven't, so no guarantees):
{_x AddEventHandler ["Hit", {_this select 0 setdamage 0.8}]} ForEach whoever
In fact, you would probably ditch the loop and just use the script to add the eventhandler to whoever activates the trigger? The eventhandlers will stay with their zombies and take care of matters thereafter.
Reservations are:
Damage of 0.8 may make for a too-easy kill;
May need to do RemoveAllEventHandlers (check the syntax) before the 'add' if a zombie can reactivate the trigger.
Have a look at Igor Drukov's Event Handler notes in the resources.